天天看点

fuse 开源代码学习心得

一,相关的理论知识准备

 1,Linux Vfs的基本概念及相关知识,比较重要的如Linux文件系统的Vfs结构、Supper超级块、inode索引节点、

    dentry目录项,可学习参考"./docs"目录下从互联网上搜集到的相关资料文档

 2, Fuse用户空间文件系统的基本概念和相关知识,比较重要的是要弄清楚什么是用户空间系统,它与传统的文

    件系统有什么不同?其优点又在哪里?

 3,Linux驱动开发的相关的相关知识,关于linux的驱动不需要了解的十分详细,我们只要弄清驱动的工作方式

    及内部大逻辑结构即可。

    Linux驱动的组织结构大逻辑如下(针对块设备,也是我们fuse的驱动工作模式):

  ------------------

  | dirver buffer  |  ---> 驱动缓存buffer

  ------------------

  | read device    |

  | write device   |

  | poll device    |  ---> 驱动的访问接口

  | release device |

  |      ...       |

  ------------------

    驱动的工作模式如下:

  内核或则应用层程序通过"驱动的访问接口"来操作设备(这里的设备可能是概念上的、虚的,不一定是真

  是的物理上的设备,如我们的fuse的驱动)(如对设备的读取或写入可通过),设备驱动程序可通过"驱动

  缓存buffer"来与访问它的程序进行数据交互。

二,代码的大结构文件及关键结构体定义说明

 基于fuse-2.7.3的代码主要有三部分组成:

 <2.1> fuse driver

  对应目录: "fuse-2.7.3/kernel"

  文件说明:

  inode.c   ---> 主要完成fuse文件驱动模块的注册,提供对supper block的维护函数以及其它

          (驱动的组织开始文件)

  dev.c     ---> fuse 的(虚拟)设备驱动

  control.c ---> 提供对于dentry的维护及其它(详细作用请阅读代码)

  dir.c   ---> 主要提供对于目录inode索引节点的维护

  file.c    ---> 主要提供对于文件inode索引节点的维护

  *.h       ---> .h文件这里不在具体说明 

 <2.2> fuse library

  对应目录: "fuse-2.7.3/lib"

  文件说明:

  helper.c  ---> "fuse_main()"调用的主入口

  fuse_kern_chan.c ---> 主要实现fuse应用层访问(读写)fuse driver的功能

  mount_util.c --> 提供mount的基础函数调用

  mount.c  ---> 主要实现设备的"mount"、"umount"等挂接操作

  mount_bsd.c ---> “Free bsd"下的"mount"、"umount"实现

  fuse_mt.c  ---> fuse 的mount管理

  fuse.c   ---> lib库主框架文件,实现了主要框架及对"用户实现的文件系统操作代码"的封装

  fuse_lowlevel.c  --> 实现比较底层的函数封装,供fuse.c等使用

  fuse_loop.c ---> fuse lib循环监视"fuse driver"的通信缓存

  fuse_loop_mt.c ---> 同上

  fuse_session.c ---> fuse会话管理

  特别说明:以上只描述对于影响工作框架结构的相关重要的.c文件,对于其它补充外围性质的.c和

     所有.h文件这里不在做详细描述,各文件的具体功能和作用详细请参考代码

 <2.3> user application base on fuse

  对应目录: "fuse-2.7.3/example"和"./OpenFsBaseOnFuse",代码比较简单,主要似乎基于fuse的上层

  实例应用,具体可参考代码

 关键的结构体说明:

 <2.4> struct fuse_req(fuse_i.h) ---> 发往客户端的请求

       说明:这里的客户端可理解为"<2.3> user application base on fuse",以下同

 <2.5> struct fuse_session(fuse_session.c) ---> 客户端管理会话的结构体,其包含"struct fuse_chan"结构

 <2.6> struct fuse_chan(fuse_session.c) ---> 定义客户端与fuse内核连接隧道的结构体

       特别说明:这里的连接隧道不是什么具体的网络连接,而是客户端通过fuse设备驱动来读写设备缓存(以与设备交互)

   的一条概念上的隧道

 说明:以上的几个结构体主要在程序的流程通信架构中比较重要,所以这里特别指出说明

三,代码框架公走流程示意("-+"表示数据流方向, "-|*"号表示注解说明)

 <3.1>fuse文件系统的挂载

      关于使用fuse的"mount"和"umount"过程,相对比较简单(代码也比较容易读懂),这里不在过多描述,

             只特别指出以下几点:

      <3.1.1>"#modprobe fuse",安装完fuse driver驱动后,系统并没有建立可维护的supper block超级块

                    (只是建立了可用的设备驱动)到系统自身维护的超级块链表,而是在有mount挂载动作后,先建立

      了"vfsmount"到"vfs"维护的mount链表,随后调用注册文件系统时的"fuse_fill_super"(inode.c)

      来创建超级块,创建完成后系统会自动添加它到其维护的supper block链表中。

             <3.1.2>"umount"的过程与"mount"的过程相反,这里不在特别说明。

 <3.2>fuse文件系统挂载后的使用驱动工作流程(消息传递)示意如下(重要):

 --------------------

 |#Linux 用户指令   |----------------------------------------------------------------------|* user command

 --------------------

  1,req|    +11,resp

       +    |

 -------------------- 2,req  -------------------------

 |                  | -----+ | Vfs Filesystem        |

        |Linux kernel      |        |*1, check vfsmount     |-------------------------------------|* Linux system kernel

 |                  | -----+ |*2, read device driver |

 ------------------- 10,resp -------------------------

          3,req|        +9,resp  

        +     | 

      -----------------------------------------------------------

      |fuse kernel driver                                       | 

      |*1, put operation request for "inode" to "driver buffer" |-----------------/

      |*2, return operation response for "inode" to "Vfs"       |                  /

      -----------------------------------------------------------                   /

          4,put req|                            +8,return resp                       |* "<2.1> fuse driver"

            +                            |                                   /

      -----------------------------------------------------------                  /

      |fuse kernel driver buffer                               |-----------------/

      ----------------------------------------------------------- 

          5,read request+                     +7,write result

          |                     |

    ---------------------------------------

           | fuse lib session(channel)           |----------------------------| "fuse_kern_chan.c"

    ------------------------ ---------------------------------------

    |user application base |    | fuse lib process(fuse_ll_process()) | 

    |fuse     |--> |* process request(perform it)        |----------------------------|* "<2.2> fuse library"

    |*(user space)     | |  |* return perform result              |

    ------------------------ | ---------------------------------------

              |              -

              |              *

              -    6, "fuse_ll_ops[]" call support

              *

   "<2.3> user application base on fuse"   

四,实例函数调用流程

 The following diagram shows how a filesystem operation (in this example unlink) is performed in FUSE.

NOTE: everything in this description is greatly simplified

 |  "rm /mnt/fuse/file"               |  FUSE filesystem daemon

 |                                    |

 |                                    |  >sys_read()

 |                                    |    >fuse_dev_read()

 |                                    |      >request_wait()

 |                                    |        [sleep on fc->waitq]

 |                                    |

 |  >sys_unlink()                     |

 |    >fuse_unlink()                  |

 |      [get request from             |

 |       fc->unused_list]             |

 |      >request_send()               |

 |        [queue req on fc->pending]  |

 |        [wake up fc->waitq]         |        [woken up]

 |        >request_wait_answer()      |

 |          [sleep on req->waitq]     |

 |                                    |      <request_wait()

 |                                    |      [remove req from fc->pending] |                                    |      [copy req to read buffer]

 |                                    |      [add req to fc->processing] |                                    |    <fuse_dev_read()

 |                                    |  <sys_read()

 |                                    |

 |                                    |  [perform unlink]

 |                                    |

 |                                    |  >sys_write()

 |                                    |    >fuse_dev_write()

 |                                    |      [look up req in fc->processing] |                                    |      [remove from fc->processing] |                                    |     [copy write buffer to req]

 |          [woken up]                |      [wake up req->waitq]

 |                                    |    <fuse_dev_write()

 |                                    |  <sys_write()

 |        <request_wait_answer()      |

 |      <request_send()               |

 |      [add request to               |

 |       fc->unused_list]             |

 |    <fuse_unlink()                  |

 |  <sys_unlink()                     |

There are a couple of ways in which to deadlock a FUSE filesystem. Since we are talking about unprivileged userspace programs, something must be done about these.

详细可参考:"fuse2.7.3/doc/kernel.txt",以上交互流程报文引至该文件

特别说明:该流程完整的描述了一个基本的"rm"命令在"fuse"文件系统上的函数调用过程,而其它比较复杂的文件操作指令也都是由一个或多个这样的基本指令组合出来。

五,心得体会

fuse用户空间文件系统与真实的文件系统(ext2/ext3/fat, 关于这些文件系统的知识请查阅其它相关资料文档,这里不在累述)不同,它的supper block, indoe, dentry等都是由内存

虚拟而来,具体在物理磁盘上存储的真实文件结构是什么,它不关心,且对真实数据的请求通过驱动和接口一层层传递到用户空间的用户编写的具体实现程序里来,这样就为用户开发自

己的文件系统提供了便利,这也就是所谓的“用户空间文件系统”的基本工作理念。

六,补充说明

 由于本人水平和本阶段对代码理解的目标要求及时间的局限,以上解析如果出现偏差,请多多谅解并以实际情况为准,本文档是我在阅读fuse2.7.3源代码

后对一些在理解大框架结构时、针对一些比较难理解的关键点的说明,旨在记录下当初的想法,以便日后参考佐证,希望能对您的学习能有所帮助。

继续阅读