一 Fuse简要介绍
1 什么是Fuse
传统的文件系统是操作系统的一部分,放在操作系统内核里面实现。Fuse(Filesystem in Userspace), 一个用户空间文件系统框架,提供给我们一组用于实现一个文件系统的API,使我们可以在用户态实现自已的文件系统。目前fuse已集成在Linux2.6以上版本的内核中。
(注:操作系统中的用户态指权限等级中的一般级别,与之相对的是超级用户或者管理员的特权级别。用户态启动的每个进程,根据运行该进程的登录用户,都被系统赋予一定的权限,另外也有一些限制。)
2 优缺点
1)传统文件系统都是定义在操作系统内核层面上的,要操作系统识别一种新的文件系统,必需重写内核,而内核态代码难以调试,生产率较低;但是用户空间编程和调试难度较小,有更多的语言可以选择(目前FUSE已经绑定了很多语言,比如c++、java等),还可以复用已有的库),从而能够大幅提高生产率,极大地简少了为操作系统提供新的文件系统的工作
量。
2)一些服务可以通过统一的文件系统接口来进行访问,比如说ftp、sftp、samba
3)可以把非文件的服务当做文件来实现,比如把gmail提供的巨大的空间用来进行文件存储的Gmail Filesystem。
在用户态实现文件系统必然会引入额外的内核态/用户态切换带来的开销,对性能会产生一定影响。
二 Fuse的结构
fuse包括三个模块:用户空间库,内核模块以及mount工具
1)用户空间库给程序员提供编程接口,程序员通过实现fuse提供的两组接口fuse_lowlevel_ops, fuse_operations之一即可实现一个用户空间文件系统
2)内核模块实现了一个完整文件系统的框架,但具体操作没有实现(由程序员在用户空间实现)
3)mount工具fusermount用于挂载基于fuse的文件系统
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/fuse
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返回调用。
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 */
pending
    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_readv(struct file *file, const struct iovec *iov,  unsigned long nr_segs, loff_t *off)                             
{
    ….
    request_wait(fc);
….
}
/* Wait until a request is available on the pending list
*当前进程一直等待,直到挂起队列中有一个请求
*/
static void request_wait(struct fuse_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队列,看是否有请求
static int request_pending(struct fuse_conn *fc)
{
return !list_empty(&fc->pending) || !list_empty(&fc->interrupts);
}
(右列一到四)request_send是用户请求经过vfs(如上面的图),再到fuse operation中被调用的,它向/dev/fuse发送请求
void  request_send(struct fuse_conn *fc, struct fuse_req *req)
{
    ……
    queue_request(fc, req);
request_wait_answer(fc, req);
……
}
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。