Linux FUSE源代码分析.docx
- 文档编号:11352081
- 上传时间:2023-02-28
- 格式:DOCX
- 页数:26
- 大小:294.35KB
Linux FUSE源代码分析.docx
《Linux FUSE源代码分析.docx》由会员分享,可在线阅读,更多相关《Linux FUSE源代码分析.docx(26页珍藏版)》请在冰豆网上搜索。
LinuxFUSE源代码分析
LinuxFUSE源代码分析
一、Fuse简要介绍
FUSE(用户空间文件系统)是这样一个框架,它使得FUSE用户在用户态下编写文件系统成为可能,而不必和内核打交道。
FUSE由三个部分组成,linux内核模块、FUSE库以及mount工具。
用户关心的只是FUSE库和mount工具,内核模块仅仅提供kernel的接入口,给了文件系统一个框架,而文件系统本身的主要实现代码位于用户空间中。
FUSE库给用户提供了编程的接口,而mount工具则用于挂在用户编写的文件系统。
FUSE起初是为了研究AVFS(AVirtualFilesystem)而设计的,而现在已经成为SourceForge的一个独立项目,目前适用的平台有Linux,FreeBSD,NetBSD,OpenSolaris和MacOSX。
官方的linuxkernel版本到2.6.14才添加了FUSE模块,因此2.4的内核模块下,用户如果要在FUSE中创建一个文件系统,需要先安装一个FUSE内核模块,然后使用FUSE库和API来创建。
1.1什么是Fuse
传统的文件系统是操作系统的一部分,放在操作系统内核里面实现。
Fuse(FilesysteminUserspace),一个用户空间文件系统框架,提供给我们一组用于实现一个文件系统的API,使我们可以在用户态实现自已的文件系统。
目前fuse已集成在Linux2.6以上版本的内核中。
(注:
操作系统中的用户态指权限等级中的一般级别,与之相对的是超级用户或者管理员的特权级别。
用户态启动的每个进程,根据运行该进程的登录用户,都被系统赋予一定的权限,另外也有一些限制。
)
1.2优缺点
1)传统文件系统都是定义在操作系统内核层面上的,要操作系统识别一种新的文件系统,必需重写内核,而内核态代码难以调试,生产率较低;但是用户空间编程和调试难度较小,有更多的语言可以选择(目前FUSE已经绑定了很多语言,比如c++、java等),还可以复用已有的库),从而能够大幅提高生产率,极大地简少了为操作系统提供新的文件系统的工作量。
2)一些服务可以通过统一的文件系统接口来进行访问,比如说ftp、sftp、samba
3)可以把非文件的服务当做文件来实现,比如把gmail提供的巨大的空间用来进行文件存储的GmailFilesystem。
在用户态实现文件系统必然会引入额外的内核态/用户态切换带来的开销,对性能会产生一定影响。
二、FUSE特性
a、库文件和API简单,极大地方便了用户的使用
b、安装简便,不需要加补丁或者重新编译kernel
c、执行安全,使用稳定
d、高效,相对于其它用户态文件系统实例
e、非特权用户可以使用
f、基于linux2.4.x和2.6.x内核,现在可以支持JavaTM绑定,不必限定使用C和C++来编写文件系统
三、源代码目录:
./doc包含FUSE相关文档
./include包含了FUSEAPI头,对创建文件系统有用,主要用fuse.h
./lib存放FUSE库的源代码
./util包含了FUSE工具库的源代码
./example参考的例子
四、安装
FUSE的源码安装类似于其他软件,只需要在FUSE的源码目录下执行如下命令即可:
./configure
make
makeinstall(以root身份执行)
五、FUSEoperations
FUSE使用fuse_operations来给用户提供编程结构,让用户通过注册自己编写的函数到该结构体来实现自己的文件系统。
六、Fuse文件系统的结构
fuse包括三个模块:
用户空间库,内核模块以及mount工具
1)用户空间库给程序员提供编程接口,程序员通过实现fuse提供的两组接口fuse_lowlevel_ops,fuse_operations之一即可实现一个用户空间文件系统
2)内核模块实现了一个完整文件系统的框架,但具体操作没有实现(由程序员在用户空间实现)
3)mount工具fusermount用于挂载基于fuse的文件系统
6.1Fuse在用户空间工作的流程图
通过这幅图可以看到三个模块在fuse工作时所起的作用
fuse_main()(lib/helper.c)——fuse用户空间主函数,用户程序调用它时,fuse_main()函数解析相关参数(如mountpoint,multithreaded),并调用fuse_mount()函数,接着调用fuse_new()函数,为fuse文件系统数据分配存储空间。
最后调用fuse_loop()函数实现会话的接受与处理。
fuse_mount()(lib/mount.c)——创建UNIX本地套接口,创建并运行子进程fusermount。
fusermount(util/fusermount.c)——确保fuse模块已经加载,通过UNIX套接口返回fuse模块的文件fd给fuse_mount()函数。
fuse_new()(lib/fuse.c)——为fuse创建数据结构空间,用来存储文件系统数据。
fuse_loop()(lib/fuse.c)(fuse_loop_mt()(lib/fuse_mt.c))——从/dev/fuse(/dev设备文件存储目录)读取文件系统调用,调用fuse_operations或fuse_lowlevel_ops结构中的处理函数,返回调用结果给/dev/fuse
6.2Fuse内核模块
FUSEKernel模块由两部分组成:
第一部分——proc文件系统组件:
Kernel/dev.c——回应io请求到/dev/fuse。
fuse_dev_read()函数负责读出文件,并将来自“listofrequest”结构体的命令返回到调用函数。
fuse_dev_write()负责文件写入,并将写入的数据置放到“req→out”数据结构中。
第二部分——文件系统调用部分:
kernel/file.c,kernel/inode.c,kernel/dir.c——调用request_send(),将请求加入到“listofrequest”结构体中,等待回复(reply)。
七、Fuse调用流程
由于fuse处理请求过程涉及的内容较多,如果从采用从外到内逐层深入的方法来讲,虽然符合逻辑但会增加理解难度,因为到最后大家会迷失在一个个的函数调用里,而且也难以抓住其本质与核心。
所以我由其核心——队列管理讲起,向外扩散;再从最外层的函数调用向内讲;最后瞻前顾后,整个fuse处理请求的流程就明白了。
我们先利用下面一幅图简要了解下fuse文件系统工作时的调用路径。
在shell里输入命令,请求通过vfs到达fuse,然后通过用户实现的fuse给出的API返回调用。
7.1Fuse处理请求的核心工作就是进行队列管理
1)两个重要的数据结构
fc的定义如下
/*AFuseconnection.
*Thisstructureiscreated,whenthefilesystemismounted,andisdestroyed,whenthe
*clientdeviceisclosedandthefilesystemisunmounted.
*/
Structfuse_conn
{
/**Readersoftheconnectionarewaitingonthis*/
wait_queue_head_twaitq;//等待执行请求的进程的队列
/**Thelistofpendingrequests*/
structlist_headpending;//被挂起的请求的队列
/**Thelistofrequestsbeingprocessed*/
structlist_headprocessing;//正在被处理的请求的队列
/**Pendinginterrupts*/
structlist_headinterrupts;//执行中被中断的请求的队列
...
}
req的定义如下:
/*
*Arequesttotheclient
*/
structfuse_req
{
/**Usedtowakeupthetaskwaitingforcompletionofrequest*/
wait_queue_head_twaitq;//请求的等待队列
…
}
2)队列管理的过程如下
3)队列管理的相关代码
①(左列一至五行)fuse通过fuse_session_loop来启动守护程序,守护程序最终会调用fuse_dev_readv,fuse_dev_readv调用request_wait,使得进程在fc的waitq队列上睡眠。
Staticsize_tfuse_dev_readv(structfile*file,conststructiovec*iov,unsignedlongnr_segs,loff_t*off)
{
….
request_wait(fc);
….
}
②/*Waituntilarequestisavailableonthependinglist
*当前进程一直等待,直到挂起队列中有一个请求
*/
staticvoidrequest_wait(structfuse_conn*fc)
{
DECLARE_WAITQUEUE(wait,current);//定义一个队列节点变量wait,其与当前进程相关联
add_wait_queue_exclusive(&fc->waitq,&wait);//将wait加入到fc->waitq等待队列中
//不断的检查fc的pending队列及interrupts队列,看是否有请求,没有请求一直while循环
while(fc->connected&&!
request_pending(fc))
{
set_current_state(TASK_INTERRUPTIBLE);
if(signal_pending(current))break;
spin_unlock(&fc->lock);
schedule();//选择一个进程运行
spin_lock(&fc->lock);
}
//有请求,将进程设为TASK_RUNNING状态(被唤醒,被赋予CPU使用权)
set_current_state(TASK_RUNNING);
remove_wait_queue(&fc->waitq,&wait);//将wait(当前进程)从等待队列中移除
}
③//fc的pending队列及interrupts队列,看是否有请求
staticintrequest_pending(structfuse_conn*fc)
{
return!
list_empty(&fc->pending)||!
list_empty(&fc->interrupts);
}
④(右列一到四)request_send是用户请求经过vfs(如上面的图),再到fuseoperation中被调用的,它向/dev/fuse发送请求
voidrequest_send(structfuse_conn*fc,structfuse_req*req)
{
……
queue_request(fc,req);
request_wait_answer(fc,req);
……
}
⑤staticvoidqueue_request(structfuse_conn*fc,structfuse_req*req)
{
list_add_tail(&req->list,&fc->pending);//将请求加入到pending队列
req->state=FUSE_REQ_PENDING;
if(!
req->waiting)
{
req->waiting=1;
atomic_inc(&fc->num_waiting);
}
wake_up(&fc->waitq);//唤醒等待等列
kill_fasync(&fc->fasync,SIGIO,POLL_IN);
}
⑥/*Calledwithfc->lockheld.Releases,andthenreacquiresit.*/
//该调用会在req的waitq上睡眠,fuse守护程序处理完请求后,会将其唤醒
staticvoidrequest_wait_answer(structfuse_conn*fc,structfuse_req*req)
{
if(!
fc->no_interrupt)
{
/*Anysignalmayinterruptthis*/
wait_answer_interruptible(fc,req);
if(req->aborted)
gotoaborted;
if(req->state==FUSE_REQ_FINISHED)
return;
req->interrupted=1;
if(req->state==FUSE_REQ_SENT)
queue_interrupt(fc,req);
}
if(req->force){
spin_unlock(&fc->lock);
wait_event(req->waitq,req->state==FUSE_REQ_FINISHED);
spin_lock(&fc->lock);
}else{
sigset_toldset;
/*Onlyfatalsignalsmayinterruptthis*/
block_sigs(&oldset);
wait_answer_interruptible(fc,req);
restore_sigs(&oldset);
}
if(req->aborted)
gotoaborted;
if(req->state==FUSE_REQ_FINISHED)return;
req->out.h.error=-EINTR;
req->aborted=1;
aborted:
if(req->locked){
/*Thisisuninterruptiblesleep,becausedatais
beingcopiedto/fromthebuffersofreq.During
lockedstate,theremustn'tbeanyfilesystem
operation(e.g.pagefault),sincethatcouldlead
todeadlock*/
spin_unlock(&fc->lock);
wait_event(req->waitq,!
req->locked);
spin_lock(&fc->lock);
}
if(req->state==FUSE_REQ_PENDING){
list_del(&req->list);
__fuse_put_request(req);
}elseif(req->state==FUSE_REQ_SENT){
spin_unlock(&fc->lock);
wait_event(req->waitq,req->state==FUSE_REQ_FINISHED);
spin_lock(&fc->lock);
}
}
}
(左列七行)fuse守护程序处理完请求,最终通过fuse_dev_writev写回/dev/fuse,它将唤醒相应req中waitq的等待队列元素,从而让文件系统请求完成request_wait_answer,获取到结果。
⑦/**Writeasinglereplytoarequest.Firsttheheaderiscopiedfromthewritebuffer.Therequestisthen*searchedontheprocessinglistbytheuniqueIDfoundintheheader.Iffound,thenremoveitfromthelist*andcopytherestofthebuffertotherequest.Therequestisfinishedbycallingrequest_end()
*/
staticssize_tfuse_dev_writev(structfile*file,conststructiovec*iov,unsignedlongnr_segs,loff_t*off)
{
……..
req=request_find(fc,oh.unique);
request_end(fc,req);
….
}
⑧/**Thisfunctioniscalledwhenarequestisfinished.Eitherareplyhasarrivedoritwasaborted(andnotyet*sent)orsomeerroroccurredduringcommunicationwithuserspace,orthedevicefilewasclosed.The*requesterthreadiswokenup(ifstillwaiting),the'end'callbackiscalledifgiven,elsethereferencetothe*requestisreleasedCalledwithfc->lock,unlocksit
*/
staticvoidrequest_end(structfuse_conn*fc,structfuse_req*req)
{
….
wake_up(&req->waitq);//唤醒req上的等待队列
……
}
7.2fuse处理请求流程
以unlink操作为例,根据流程图结合源代码分析fuse处理请求流程
处理unlink操作的整个流程如下图所示。
其中“>”表示调用,”<”表示返回,[]表示调用中所做的工作。
①fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。
intfuse_session_loop(structfuse_session*se)//在fuse_main中会被调用,或其多线程版本
{
intres=0;
structfuse_chan*ch=fuse_session_next_chan(se,NULL);
size_tbufsize=fuse_chan_bufsize(ch);
char*buf=(char*)malloc(bufsize);//为channel分配好缓冲区
if(!
buf){
fprintf(stderr,"fuse:
failedtoallocatereadbuffer\n");
return-1;
}
//fusedaemon,loops
while(!
fuse_session_exited(se)){
structfuse_chan*tmpch=ch;
//从/dev/fuse读请求,会等待一直到有请求为止
res=fuse_chan_recv(&tmpch,buf,bufsize);
if(res==-EINTR)continue;
if(res<=0)break;
fuse_session_process(se,buf,res,tmpch);//处理读到的请求
}
free(buf);
fuse_session_reset(se);
returnres<0?
-1:
0;
}
②intfuse_chan_recv(structfuse_chan**chp,char*buf,size_tsize)
{
structfuse_chan*ch=*chp;
if(ch->compat)
return((structfuse_chan_ops_compat24*)&ch->op)->receive(ch,buf,size);
else
returnch->op.receive(chp,buf,size);//由下面的一段代码可以发现,receive最终是通过
//fuse_kern_chan_receive实现的,代码片段3分析该请求
}
③#defineMIN_BUFSIZE0x21000
structfuse_chan*fuse_kern_chan_new(intfd)
{
//channel的读写方法
structfuse_chan_opsop={
.receive=fuse_kern_chan_receive,
.send=fuse_kern_chan_send,
.destroy=fuse_kern_chan_destroy,
};
//设置bufsize大小
size_tbufsize=getpagesize()+0x1000;
bufsize=bufsize MIN_BUFSIZE: bufsize; returnfuse_chan_new(&op,fd,bufsize,NULL); } ④staticintfuse_kern_chan_receive(structfuse_chan**chp,char*buf,size_tsize) { structfuse_chan*ch=*chp; interr; ssize_tres; structfuse_session*se=fuse_chan_session(ch); assert(se! =NULL); //一直轮询,直到读到请求为止 restart: //fuse_chan_fd获取到/dev/fuse的文件描述符,调用read系统调用从设备读取请求 res=read(fuse_chan_fd(ch),buf,size);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux FUSE源代码分析 FUSE 源代码 分析