1、linukernelfuse源码剖析FUE源码剖析1、前言 本文就是对USE、9、源码得学习总结。FUS代码在用户空间与内核空间都有运行,为了突出重点,先简要描述了在基于FSE得用户空间文件系统中执行write操作得一般流程,接下来介绍了 重要得数据结 构,最后以F得运行过程为线索,剖析FUSE程序运行过程得3个关键步骤:1、FUSE模块加载2、n与pn过程、对文件write。对于虚拟文件系统与设备驱动得相关概念本文仅作简要说明。需要说明得就是,由于内核得复杂性及个人能力得有限,本文省略了包括内核同步,异常检查在内得诸多内容,希望可以突出重点.、FSE下ite得一般流程 图在基于FUSE得用户
2、空间文件系统中执行wr操作得流程如图所示(由于版面关系,图中部分函数就是缩写,请参考源码):1、客户端在mout目录下面,对一个regularfil调用rt,这一步就是在用户空间执行、wre内部会调用虚拟文件系统提供得一致性接口vfswrite、根据FUSE模块注册得信息,vs_wrt会调用ue_,将写请求放入fuecnetion得rqustndngquee,随后进入睡眠等待应用程序rely4、用户空间得libfuse有一个守护进程通过函数fus_sesion_oo轮询杂项设备dev/use,一旦reutque有请求即通过fus_kern_haneive接收5、se_ken_chan_rece
3、iv通过rea读取reustuu中得内容,red系统调用实际上就是调用得设备驱动接口fse_d_red、在用户空间读取并分析数据,执行用户定义得wite操作,将状态通过use_epl_writ返回给kernel7、se_rly_rit调用VF提供得一致性接口fsrte8、vfs_ite最终调用use_dev_write将执行结果返回给第3步中等待在it得进程,此进程得到reply后,wte返回、数据结构本节主要介绍了FSE中比较重要得数据结构,需要说明得就是图示中只列出了与叙述相关得数据成员,完整得数据结构细节请参考源码。3、内核部分 图2trutfus_conn:每一次mount会实例化一个
4、structfuse_conn即fusconnection,它代表了用户空间与内核得通信连接。fueconnctio维护了包括pningli,ocessings与ioli在内得reuestqeue,fseconnection通过这些队列管理用户空间与内核空间通信过程。ucfusereq:每次执行系统调用时会生成一个tructfe_req,这些fue_re依据tte被组织在不同得队列中,trutfuse_onn维护了这些队列、structile:存放打开文件与进程之间进行交互得有关信息,描述了进程怎样与一个打开得文件进行交互,这类信息仅当进程访问文件期间存在于内核内存中。sutnode:文件系统
5、处理文件所需要得所有信息都放在一个名为inoe(索引节点)得数据结构中。文件名可以随时更改,但就是索引节点对文件就是唯一得,并且随着文件得存在而存在.trt:定义了可以对文件执行得操作。、2、用户空间部分 图3sructsereq:这个结构与上文中内核得fus_req同名,有着类似得作用,但就是数据成员不同。structfus_sssio:定义了客户端管理会话得结构体,包含了一组对sssin可以执行得操作。structfuse_can:定义了客户端与FUSE内核连接通道得结构体,包含了一组对cannel可以执行得操作。sructse_ll_o:结构得成员为一个函数指针unc与命令名字符串am,
6、内核中发过来得每一个rquet最后都映射到以此结构为元素得数组中.4、FUS模块加载USE内核模块需要在用户空间使用imod或者modprbe加载.它们通过系统调用init_modue启动加载过程,注册过程比较简单,包括如下步骤:1、创建高速缓存结构fuse_neap2、遍历链表,如果未注册,则将fusebkstype链到链表尾部3、遍历链表,如果未注册,则将ue_fstye链到链表尾部4、创建us_ko与onectinkobj两个kobject5、遍历链表,如果未注册,则将fuse_cl_s_type链到链表尾部模块成功加载以后,以下接口被注册234567891011123141511718
7、1921223225627282903333343tati stt fubk_f_type= /块设备、ownr= _MODULE,、nam fuselk,、nt= fuse_mount_lk,、kl_s=use_kllsb_blk,、s_flag = SREUIE_DEV F_HAS_SUBYPE,;stticsruct fse_fs_type =、owerTHS_MODUE,、nae us,、s_flas = F_AS_SUYP,、ount= fse_un,、kil_b=fuse_killsb_non,;cnst strc fu_dev_oprin = 、wer=THIS_MOU,、lee
8、=nolseek,、ea = o_ync_e,、io_read fuse_dv_read,、sliceread fseevpliceread,、write= o_synwite,、ao_wrie fus_dewite,、si_wrie=fuse_evsplice_write,、ol =fus_de_pl,、eleae= fuse_d_rease,、fayc = ue_dev_fasn,;static stru miscdvcusmscevc = 、inr = FSE_MINOR,、nam= ”use,、fos= &u_dv_operations,;5、munt与open过程FSE模块加载注册了
9、fuselk_typ与fus_fs_tp两种文件类型,默认情况下使用得就是fefs_tpe即mut函数指针被初始化为fuse_mont,而fusent实际调用ont_noev,它主要由如下两步组成:、sget(fs_typ)搜索文件系统得超级块对象(super_bok)链表(tyefs_sps),如果找到一个与块设备相关得超级块,则返回它得地址.否则,分配并初始化一个新得超级块对象,把它插入到文件系统链表与超级块全局链表中,并返回其地址.2、filsuper(此函数由各文件系统自行定义):这个函数式各文件系统自行定义得函数,它实际上就是use_fil_sper。一般fll_super会分配索引
10、节点对象与对应得目录项对象,并填充超级块字段值,另外对于fus还需要分配fus_con,fue_rq。需要说明得就是,它在底层调用了ue_in_用fuse_与use_分别初始化no-ifop与od-_data、ops。123456781113115171819022324267829taic onst trct fuse = 、llseek=use_,、re = do_syn_rea,、ao_read fue_,、rite= dosync_write,、aio_wrie= fs_,、map fus_,、ope = fuse_open,、flush=fue_ush,、reease fserele
11、se,、fsyncfu_fsyc,、o fus_,、lock use_,、splce_read= geeric_,、nocdioctl us_,、pat_ocl = ue_,、pol =fuse,、flocate fuse_,;ttc onst ruct address_pceoeaion use_=、radag = fus_reapa,、wrtepae= fus_writepge,、launder_page= _landerpag,、readpges=fe_radag,、e_ge_iy = _set_ae_diry_nbuffers,、bmp =fue_ap,、diect_I= fuse_i
12、rect_IO,;opn系统调用底层实现相当复杂,它得主要工作就是实例化i对象.fi-p就就是在open中被赋值为inoe-i_fop,这一过程读者可以在s/pen、中得doetr_pn函数中找到.如上所述,ie_op已经被e_ni_初始化为fs_。至此,普通文件与设备文件得操作接口都已成功初始化.、FUSE用户空间流程USE在用户空间提供了fueuserpeibrary与out/umnt.fueuespacelibrar提供了一组AP供用户开发用户空间文件系统。用户要做得就就是实现fue_operations或se_lwevl_ps定义得操作,这两个结构类似于FS中得strut。mount工
13、具fusermout用于挂载用fue实现得文件系统。用户在使用fse得时候有两种开发模式:一种就是ghvel模式,此模式下fs得入口函数为fuse_main,它封装了一系列初始化操作,使用简单,但就是不灵活。另一种就是lowleve模式,用户可以利用fue提供得底层函数灵活开发应用程序。需要说明得就是higlv模式其实就是对lwlevel得封装,因此这里分析olevel模式. 图图4展示FUS在用户空间总体工作流程:、调用fue_munt实例化sructfu_can为ch,将指定目录unt到挂载点2、实例化trtfs_sesso为e,并且将se与c关联3、进入循环,从ev/fuse读取数据,处
14、理以后执行响应得操作 图图5展示了use_mont函数内部流程:1、确保打开得文件描述符至少大于2、分析并检查用户传入得参数、打开/ev/fe得到fd,用户空间与内核通过/de/fuse通信4、mount源目录到挂载点5、用fd实例化strutfus_chan为ch6、返回ch 图图6展示了use_mout_t25内部细节,进入循环以后,函数fuse_esi_eceeb实际通过fse_ll_eceive_bf从/dev/fuse中读取数据,其通过bf返回。use_llreceveuf就是通过ead或者plice系统调用从内核reuest队列中读取数据。函数fuse_sessonprocess_
15、b实际通过use_ll_process_buf处理数据,fell_processbu会根据数据类型最后执行用户定义得操作felosin-opd、fu(,in-noded,na).执行完用户定义得操作以后需要向内核返回执行结果,fuse提供了一组类似fseply_X得API,这些API最后实际通过系统调用writev将结果传入内核。7、E内核部分流程FSE在内核空间执行得部分主要包括FUSE模块加载以及杂项设备驱动。模块加载过程已经在第4节介绍,这一节主要描述从reues队列读写请求得流程.FSE设备驱动程序本质上就是一个生产者消费者模型。生产者为用户在挂载目录下对普通文件(gularfile)
16、执行得系统调用,每一次系统调用会产生一个reqet然后将去放入pendinglist。pendglist能存放得元素个数只与系统内存有关;消费者为用户对设备文件/dev/se或者de/fuseblk得ra,这一操作会去ndinlis或interruist取reus,当is为空时,进程主动schedle让出CP。qust结构得细节在第3节已经介绍,此处不赘述。enmufueq_ta定义了equet得6种状态,其含义分别为:FSEREQ_INT:请求被初始化_RE_PENDIN:请求挂起待处理FSE_REQ_EADNG:请求正在读FUSE_RE_SET:请求被发送FUE_EQ_WRITING:请求
17、正在写FUSE_Q_FIISH:请求已经完成 图图就是在mout目录下面执行wie以后触发得一个函数调用序列,图中省略了S层得函数调用。fs_就是在mot过程中注册到fse_得函数指针,它会调用fuse_perform_rite,feperorm_wrte调用et_fuseconn得到strutfuse_conn实例,它保存在tucter_lck得私有数据成员中s_fs_inf中,而trcter_lok就是srcinode得一个成员。接下来就是循环从用户空间拷贝数据到内核,数据实际保存在strctpgs中,内核fse_rq保存了pges指针,然后调用fuse_sendwite_gs. 图Fse
18、_endwritg调用会等待脏数据写回到磁盘上,然后调用s_write_il将包括操作码FSE_WRITE在内得信息写入equt。随后f_reus_send(fc,req),它先通过use_ge_uniu获取唯一请求号,请求号就是一个4位无符号整数,请求号从1开始随请求依次递增。然后调用quue_reques(c,e),它主要完成件事情:1、将reques-list插入fc维护得ndin链表尾部2、置eqsate为FUSRE_ENDING、wa_up唤醒等待队列fcaitq、kill_fyn异步通知用户进程数据到达从quue_qust返回以后调用request_witanswr:进程被投入睡眠
19、,等待请求完成(wait_ent(reqstt=USE_REQ_FISHE)。如果用户程序处理完了请求,它会epl,进程被唤醒,到此可以向上层调用返回处理结果(错误码或者写入字节数)。在第节我们提到了用户空间有个demn进程会循环read设备文件fue/dev以便处理内核请求,图展示了该rea调用触发得函数调用序列. 图9从第4节可知,FUSE模块加载过程注册了对设备文件d/fuse得操作接口fue_ev_opeatin。由此可知,ed底层实际调用得就是fuse_e_readfus_dev_ead首先通过use_get_conn获得strucuse_con得实例fc,通过fseop_ni为ru
20、tfus_cystate分配内存并将其实例化。主要得数据读取在fusdevdo_read中分4步完成:1、requestwit:在挂起得列表上等待一个请求到达:(1)、DECR_WAITEUE(wi,curret):创建等待队列项,并将其初始化为curent()、addwat_que_usve(fcaitq,&wat):将wat加入fcwaitq,当有请求发送到US文件系统时,这个等待队列上得进程会被唤醒(3)、如果没有request,一直循环检查edinist与iterruptlis,直到有请求;如果有请求则将state设置为AK_UNG(4)、将wit从等待队列中移除2、lit_entry
21、(fcpning、next,stfuse_req,lis):从f-nding、nxt中取出equest,req-state状态设为FUS_R_REAI,3、将req-list移到fco4、fuse_co_one:将数据拷贝到ttfuse_copy_tte得bu中(此buf指针指向应用层得vidbf),返回。阅读代码时需要注意:usede_red:srucfuse_pysta成员wri为;fuse_de_te:structfse_coptae成员ri为.用户读取eqet,分析并执行以后需要调用fse_wte_rpl回复内核,这个函数最终调用rite写/de/fuse。图1就是wr触发得函数调用序列。图10wit前两步与read类似即获取fc(tructfusecon)与实例化cs(ructf_cystat)实际
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1