【Java精品源码栏目提醒】:网学会员为需要Java精品源码的朋友们搜集整理了Linux_FUSE源代码分析 - 软件工程相关资料,希望对各位网友有所帮助!
Linux FUSE 源代码分析一、Fuse 简要介绍 FUSE(用户空间文件系统)是这样一个框架,它使得 FUSE 用户在用户态下编写文件系统成为可能,而不必和内核打交道。
FUSE 由三个部分组成,linux 内核模块、FUSE 库以及 mount 工具。
用户关心的只是 FUSE 库和 mount 工具,内核模块仅仅提供 kernel 的接入口,给了文件系统一个框架,而文件系统本身的主要实现代码位于用户空间中。
FUSE 库给用户提供了编程的接口,而 mount 工具则用于挂在用户编写的文件系统。
FUSE 起 初 是 为 了 研 究 AVFSA Virtual Filesystem 而 设 计 的 , 而 现 在 已 经 成 为SourceForge 的一个独立项目,目前适用的平台有 Linux FreeBSD NetBSD OpenSolaris 和Mac OS X。
官方的 linux kernel 版本到 2.6.14 才添加了 FUSE 模块,因此 2.4 的内核模块下,用户如果要在 FUSE 中创建一个文件系统, 需要先安装一个 FUSE 内核模块, 然后使用 FUSE库和 API 来创建。
1.1 什么是 Fuse 传统的文件系统是操作系统的一部分,放在操作系统内核里面实现。
FuseFilesystem inUserspace 一个用户空间文件系统框架,提供给我们一组用于实现一个文件系统的 API,使我们可以在用户态实现自已的文件系统。
目前 fuse 已集成在 Linux2.6 以上版本的内核中。
注:操作系统中的用户态指权限等级中的一般级别,与之相对的是超级用户或者管理员的特权级别。
用户态启动的每个进程,根据运行该进程的登录用户,都被系统赋予一定的权限,另外也有一些限制。
1.2 优缺点 1) 传统文件系统都是定义在操作系统内核层面上的,要操作系统识别一种新的文件系 统,必需重写内核,而内核态代码难以调试,生产率较低;但是用户空间编程 和调试难度较小,有更多的语言可以选择(目前 FUSE 已经绑定了很多语言,比 如 c、
java 等) ,还可以复用已有的库),从而能够大幅提高生产率,极大地简 少了为操作系统提供新的文件系统的工作量。
2) 一些服务可以通过统一的文件系统接口来进行访问,比如说 ftp、sftp、samba 3) 可以把非文件的服务当做文件来实现,比如把 gmail 提供的巨大的空间用来进行文 件存储的 Gmail Filesystem。
在用户态实现文件系统必然会引入额外的内核态/用户态切换带来的开销,对性能会产生一定影响。
二、FUSE 特性a、库文件和 API 简单,极大地方便了用户的使用b、安装简便,不需要加补丁或者重新编译 kernelc、执行安全,使用稳定d、高效,相对于其它用户态文件系统实例e、非特权用户可以使用f、基于 linux2.4.x 和 2.6.x 内核,现在可以支持 JavaTM 绑定,不必限定使用 C 和 C来编写文件系统三、源代码目录:./doc 包含 FUSE 相关文档./include 包含了 FUSE API 头,对创建文件系统有用,主要用 fuse.h./lib 存放 FUSE 库的源代码./util 包含了 FUSE 工具库的源代码./example 参考的例子四、安装 FUSE 的
源码安装类似于其他软件,只需要在 FUSE 的
源码目录下执行如下命令即可: ./configure make make install(以 root 身份执行)五、FUSE operations FUSE 使用 fuse_operations 来给用户提供编程结构,让用户通过注册自己编写的函数到该结构体来实现自己的文件系统。
六、Fuse 文件系统的结构 fuse 包括三个模块:用户空间库,内核模块以及 mount 工具 1) 用 户 空 间 库 给 程 序 员 提 供 编 程 接 口 , 程 序 员 通 过 实 现 fuse 提 供 的 两 组 接 口fuse_lowlevel_ops, fuse_operations 之一即可实现一个用户空间文件系统 2)内核模块实现了一个完整文件系统的框架,但具体操作没有实现(由程序员在用户空间实现) 3)mount 工具 fusermount 用于挂载基于 fuse 的文件系统6.1 Fuse 在用户空间工作的流程图 通过这幅图可以看到三个模块在 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/fuse6.2 Fuse 内核模块 FUSE Kernel 模块由两部分组成: 第 一 部 分 ——proc 文 件 系 统 组 件 : Kernel/dev.c—— 回 应 io 请 求 到 /dev/fuse 。
fuse_dev_read函数负责读出文件,并将来自“list of request”结构体的命令返回到调用函数。
fuse_dev_write 负责文件写入,并将写入的数据置放到“req→out”数据结构中。
第二部 分—— 文件系 统调 用部分 :kernel/file.c, kernel/inode.c, kernel/dir.c——调用request_send,将请求加入到“list of request”结构体中,等待回复reply。
七、 Fuse 调用流程 由于 fuse 处理请求过程涉及的内容较多,如果从采用从外到内逐层深入的方法来讲,虽然符合逻辑但会增加理解难度,因为到最后大家会迷失在一个个的函数调用里,而且也难以抓住其本质与核心。
所以我由其核心——队列管理讲起,向外扩散;再从最外层的函数调用向内讲;最后瞻前顾后,整个 fuse 处理请求的流程就明白了。
我们先利用下面一幅图简要了解下 fuse 文件系统工作时的调用路径。
在 shell 里输入命令,请求通过 vfs 到达 fuse,然后通过用户实现的 fuse 给出的 API 返回调用。
7.1 Fuse 处理请求的核心工作就是进行队列管理1)两个重要的数据结构fc 的定义如下/ A Fuse connection. This structure is created when the filesystem is mounted and is destroyed when the client device is closed and the filesystem is unmounted. /Struct fuse_conn / Readers of the connection are waiting on this / wait_queue_head_t waitq // 等待执行请求的进程的队列 / The list of pending requests / struct list_head pending // 被挂起的请求 的队列 / The list of requests being processed / struct list_head processing // 正在被处理的请求的 队列/ Pending interrupts / struct list_head interrupts // 执行中被中断的请求的 队列...req 的定义如下:/ A request to the client /struct fuse_req/ Used to wake up the task waiting for completion of request/ wait_queue_head_t waitq // 请求的等待队列…2)队列管理的过程如下 3)队列管理的相关代码①(左列一至五行) fuse 通过 fuse_session_loop 来启动守护程序,守护程序最终会调用fuse_dev_readv fuse_dev_readv 调用 request_wait,使得进程在 fc 的 waitq 队列上睡眠。
Static size_t fuse_dev_readvstruct file file const struct iovec iov unsigned long nr_segsloff_t off …. request_waitfc ….②/ Wait until a request is available on the pending list当前进程一直等待,直到挂起队列中有一个请求/static void request_waitstruct fuse_conn fcDECLARE_WAITQUEUEwait current //定义一个队列节点变量 wait,其与当前进程相关联 add_wait_queue_exclusiveampfc-gtwaitq ampwait //将 wait 加入到 fc-gtwaitq 等待队列中 //不断的检查 fc 的 pending 队列及 interrupts 队列,看是否有请求,没有请求一直while 循环 while fc-gtconnected ampamp request_pendingfc set_current_stateTASK_INTERRUPTIBLE if signal_pendingcurrent break spin_unlockampfc-gtlock schedule //选择一个进程运行 spin_lockampfc-gtlock // 有请求,将进程设为 TASK_RUNNING 状态被唤醒,被赋予 CPU 使用权 set_current_stateTASK_RUNNING remove_wait_queueampfc-gtwaitq ampwait // 将 wait(当前进程)从等待队列中移除③// fc 的 pending 队列及 interrupts 队列,看是否有请求static int request_pendingstruct fuse_conn fcreturn list_emptyampfc-gtpending list_emptyampfc-gtinterrupts ,再到 fuse operation 中被④(右列一到四)request_send 是用户请求经过 vfs(如上面的图)调用的,它向/dev/fuse 发送请求void request_sendstruct fuse_conn fc struct fuse_req req …… queue_requestfc reqrequest_wait_answerfc req……⑤static void queue_requeststruct fuse_conn fc struct fuse_req req list_add_tailampreq-gtlist ampfc-gtpending //将请求加入到 pending 队列 req-gtstate FUSE_REQ_PENDING if req-gtwaiting req-gtwaiting 1 atomic_incampfc-gtnum_waiting wake_upampfc-gtwaitq //唤醒等待等列 kill_fasyncampfc-gtfasync SIGIO POLL_IN⑥/ Called with fc-gtlock held. Releases and then reacquires it. ///该调用会在 req 的 waitq 上睡眠,fuse 守护程序处理完请求后,会将其唤醒static void request_wait_answerstruct fuse_conn fc struct fuse_req req if fc-gtno_interrupt / Any signal may interrupt this / wait_answer_interruptiblefc req if req-gtaborted goto aborted if req-gtstate FUSE_REQ_FINISHED return req-gtinterrupted 1 if req-gtstate FUSE_REQ_SENT queue_interruptfc req if req-gtforce spin_unlockampfc-gtlock wait_eventreq-gtwaitq req-gtstate FUSE_REQ_FINISHED spin_lockampfc-gtlock else sigset_t oldset / Only fatal signals may interrupt this / block_sigsampoldset wait_answer_interruptiblefc req restore_sigsampoldset if req-gtaborted goto aborted if req-gtstate FUSE_REQ_FINISHED return req-gtout.h.error -EINTR req-gtaborted 1aborted: if req-gtlocked / This is uninterruptible sleep because data is being copied to/from the buffers of req. During locked state there mustnt be any filesystem operation e.g. page fault since that could lead to deadlock / spin_unlockampfc-gtlock wait_eventreq-gtwaitq req-gtlocked spin_lockampfc-gtlock if req-gtstate FUSE_REQ_PENDING list_delampreq-gtlist __fuse_put_requestreq else if req-gtstate FUSE_REQ_SENT spin_unlockampfc-gtlock wait_eventreq-gtwaitq req-gtstate FUSE_REQ_FINISHED spin_lockampfc-gtlock 左列七行fuse 守护程序处理完请求,最终通过 fuse_dev_writev 写回/dev/fuse,它将唤醒相应 req 中 waitq 的等待队列元素,从而让文件系统请求完成 request_wait_answer,获取到结果。
⑦/Write a single reply to a request. First the header is copied from the write buffer. Therequest is then searched on the processing list by the unique ID found in the header. If foundthen remove it from the list and copy the rest of the buffer to the request. The request is finishedby calling request_end /static ssize_t fuse_dev_writevstruct file file const struct iovec iov unsigned long nr_segsloff_t off ……..req request_findfc oh.unique request_endfc req ….⑧/ This function is called when a request is finished. Either a reply has arrived or it wasaborted and not yet sent or some error occurred during communication with userspace or thedevice file was closed. The requester thread is woken up if still waiting the end callback iscalled if given else the reference to the request is released Called with fc-gtlock unlocks it/static void request_endstruct fuse_conn fc struct fuse_req req …. wake_upampreq-gtwaitq //唤醒 req 上的等待队列 ……7.2 fuse 处理请求流程 以 unlink 操作为例,根据流程图结合源代码分析 fuse 处理请求流程 ”lt”表示返回,表示调用处理 unlink 操作的整个流程如下图所示。
其中“gt”表示调用,中所做的工作。
①fuse 通过 fuse_session_loop(或对应多线程的方法)来启动 fuse 守护程序,守护程序不断的从/dev/fuse 上读取请求,并处理。
int fuse_session_loopstruct fuse_session se //在 fuse_main 中会被调用,或其多线程版本 int res 0 struct fuse_chan ch fuse_session_next_chanse NULL size_t bufsize fuse_chan_bufsizech char buf char mallocbufsize //为 channel 分配好缓冲区 if buf fprintfstderr quotfuse: failed to allocate read buffernquot return -1//fuse daemon loops while fuse_session_exitedse struct fuse_chan tmpch ch// 从/dev/fuse 读请求,会等待一直到有请求为止 res fuse_chan_recvamptmpch buf bufsize if res -EINTR continue if res lt 0 break fuse_session_processse buf res tmpch //处理读到的请求 freebuffuse_session_resetsereturn res lt 0 -1 : 0②int fuse_chan_recvstruct fuse_chan chp char buf size_t size struct fuse_chan ch chp if ch-gtcompat return struct fuse_chan_ops_compat24 ampch-gtop-gtreceivech buf size else return ch-gtop.receivechp buf size //由下面的一段代码可以发现,receive 最终是通过// fuse_kern_chan_receive 实现的,代码片段 3 分析该请求③define MIN_BUFSIZE 0x21000struct fuse_chan fuse_kern_chan_newint fd //channel 的读写方法 struct fuse_chan_ops op .receive fuse_kern_chan_receive .send fuse_kern_chan_send .destroy fuse_kern_chan_destroy//设置 bufsize 大小 size_t bufsize getpagesize 0x1000 bufsize bufsize lt MIN_BUFSIZE MIN_BUFSIZE : bufsize return fuse_chan_newampop fd bufsize NULL④static int fuse_kern_chan_receivestruct fuse_chan chp char buf size_t size struct fuse_chan ch chp int err ssize_t res struct fuse_session se fuse_chan_sessionch assertse NULL // 一直轮询,直到读到请求为止 restart: //fuse_chan_fd 获取到/dev/fuse 的文件描述符,调用 read 系统调用从设备读取请求res readfuse_chan_fdch buf size ,read 将调用 fuse_dev_read该方法最终通//根据 fuse 设备驱动程序 file 结构的实现(dev.c)过 fuse_dev_readv//实现,根据代码中的注释,fuse_dev_read 做了如下工作:// Read a single request into the userspace filesystems buffer. This function waits until a requestis available// then removes it from the pending list and copies request data to userspace buffer.// 而 fuse_dev_read 又调用 request_wait使得进程在 fc-gtwaitq 上睡眠 if no data: goto restart ………以上的分析对应了 fuse filesystem daemon 做的第一部分工作。
当用户从控制台输入quotrm ,再/mnt/fuse/filequot时,通过 VFS(sys_unlink) 到 fuse(dir.c 中实现的 inode_operations,file.c中实现的 file_operations 中的方法都会最终调用 request_send,后面会讲到) ,这个请求最终被发到了/dev/fuse 中, 该请求的到达会唤醒正在等待的 fuse 守护程序, fuse 守护程序读取该请求并进行处理,接下来介绍处理请求所作的工作。
⑤struct fuse_session fuse_lowlevel_new_commonstruct fuse_args args const struct fuse_lowlevel_ops op size_t op_size void userdata //fuse_lowlevel_ops 在之前的文章 http://blog.chinaunix.net/u2/87570/showart_2166461.html 中已经介绍//过了,开发者实现了 fuse_lowlevel_ops 并传递给 fuse_lowlevel_common struct fuse_ll f struct fuse_session sestruct fuse_session_ops sop //最终调用的处理方法 .process fuse_ll_process //分析见代码片段 5 .destroy fuse_ll_destroy …….⑥static void fuse_ll_processvoid data const char buf size_t len struct fuse_chan ch struct fuse_ll f struct fuse_ll data struct fuse_in_header in struct fuse_in_header buf const void inarg buf sizeofstruct fuse_in_headerstruct fuse_req req //创建并初始化一个请求 req struct fuse_req calloc1 sizeofstruct fuse_req if req NULL fprintfstderr quotfuse: failed to allocate requestnquot return req-gtf freq-gtunique in-gtunique……//根据 opcode 调用 fuse_ll_ops 中相应的方法,fuse_ll_ops 的介绍// http://blog.chinaunix.net/u2/87570/showart_2166461.html fuse_ll_opsin-gtopcode.funcreq in-gtnodeid inarg 以上代码对应中流程中 perform unlink 的工作,实际上就是执行开发者实现的一组方法来完成相关的工作, 接下来就是把执行完请求后需要的数据返回, 最终是通过 send_reply 实现的,⑦static int send_replyfuse_req_t req int error const void arg size_t argsize struct iovec iov2 int count 1 if argsize iov.
上一篇:
斗地主C语言源代码(草版)
下一篇:
购书系统设计与实现论文