1 概述
Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空间中。对于读写虚拟文件系统来讲,fuse是个很好的选择。fuse包含包含一个内核模块和一个用户空间守护进程,将大部分的VFS调用都委托一个专用的守护进程来处理。
2 工作原理
Fuse用户空间文件系统与真实的文件系统不同,它的supper block, indoe, dentry等都是由内存虚拟而来,具体在物理磁盘上存储的真实文件结构是什么,它不关心,且对真实数据的请求通过驱动和接口一层层传递到用户空间的用户编写的具体实现程序里来,这样就为用户开发自己的文件系统提供了便利,这也就是所谓的“用户空间文件系统”的基本工作理念。
2.1 模块架构
FUSE分为三大模块:
Ø FUSE内核模块(内核态)
Ø LibFUSE模块(用户态)
Ø 用户程序模块(用户态)
用户程序在用户空间实现LibFUSE库封装的文件系统操作;
LibFUSE实现文件系统主要框架、对“用户实现的文件系统操作代码“的封装、mount管理、通过字符设备/dev/fuse与内核模块通信;
FUSE内核模块实现VFS接口(实现fuse文件驱动模块的注册、fuse 的(虚拟)设备驱动、提供supper block、dentry、inode的维护),接收来至后者的请求,传递给LibFUSE,LibFUSE再传递给我们用户程序的接口进行实现操作。
图一
2.1.1 源代码结构及开发库
FUSE内核模块
kernel/inode.c
kernel/dev.c
kernel/control.c —> 提供对于dentry的维护及其它
kernel/dir.c
kernel/file.c
LibFUSE模块
lib/helper.c
lib/fuse_kern_chan.c—>主要实现fuse应用层访问(读写)fuse driver的功能
lib/mount_util.c –> 提供mount的基础函数调用
lib/mount.c
lib/mount_bsd.c —> “Free bsd”下的“mount”、“umount”实现
lib/fuse_mt.c
lib/fuse.c
lib/fuse_lowlevel.c
lib/fuse_loop.c —> fuse lib循环监视“fuse driver”的通信缓存
lib/fuse_loop_mt.c —> 同上
lib/fuse_session.c —> fuse会话管理
开发库
include/fuse.h
include/fuse_common.h
include/fuse_lowlevel.h
include/fuse_opt.h
2.2 系统工作流程
fuse_main(lib/helper.c)–fuse用户空间主函数,用户程序调用它时,fuse_main函数解析相关函数(如mountpoint,multithreaded),并调用fuse_mount函数。调用fuse_new()函数,为fuse文件系统数据分配存储空间。调用fuse_loop()函数实现会话的接受与处理。
fuse_mount(lib/mount.c)–创建UNIX本地套接口,创建并运行子进程fusermount.并返回fuse模块文件fd给fuse_main()函数。
fusemount(util/fusermount.c)–确保fuse模块已经加载,通过UNIX套接口返回fuse模块的文件fd给fuse_mount()函数。
fusermount提供一系列挂载选项,如direct_io(跳过页缓存),allow_root(允许root访问挂载的文件系统),allow_other(允许其他用户访问挂载的文件系统),nonempty(允许把文件系统挂载到非空目录),big_writes(支持大于4k的写操作)等
fuse_new(lib/fuse.c)–为fuse创建数据结构空间,用来存储文件系统数据。
fuse_loop(lib/fuse.c)(fuse_loop_mt(lib/fuse_mt.c))–从/dev/fuse读取文件系统调用,调用fuse_operation结构中的处理函数,返回调用结果给/dev/fuse.
如图二:
图二
2.3 命令调用流程分析
fuse处理请求的整个流程如图三所示,以unlink操作为例进行说明。其中“>”表示调用,”<”表示返回,[]表示调用中所做的工作。
图三
fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。
2.3.1 重要数据结构
l
struct fuse_req {
};
l
struct fuse_session {
};
l
struct fuse_chan {
};
说明:这里的连接隧道不是什么具体的网络连接,而是客户端通过fuse设备驱
动来读写设备缓存(以与设备交互的一条概念上的隧道
l
struct fuse_conn
{
struct list_head pending;
struct list_head processing;
……….
};
2.3.2 代码片段1
//在fuse_main中会被调用,或其多线程版本
int fuse_session_loop(struct fuse_session *se)
{
//分析见代码片段2,从/dev/fuse读请求,会等待一直到有请求为止
}
2.3.3 代码片段2
int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
{
}
#define MIN_BUFSIZE 0×21000
struct fuse_chan *fuse_kern_chan_new(int fd)
{
};
//设置bufsize大小
}
2.3.4 代码片段3
static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
{
restart:
res = read(fuse_chan_fd(ch), buf, size);
//根据fuse设备驱动程序file结构的实现(dev.c),read将调用fuse_dev_read,该方法最终通过fuse_dev_readv实现,根据代码中的注释,fuse_dev_read做了如下工作:
Read a single request into the userspace filesystem’s buffer.
function waits until a request is available, then removes it from
the pending list and copies request data to userspace buffer.
}
2.3.5 一阶段小结
以上的分析对应了fuse filesystem daemon做的第一部分工作。当用户从控制台输入“rm /mnt/fuse/file”时,通过VFS(sys_unlink),再到fuse(dir.c中实现的inode_operations,file.c中实现的file_operations中的方法都会最终调用request_send,后面会介绍),这个请求最终被发到了/dev/fuse中,该请求的到达会唤醒正在等待的fuse守护程序,fuse守护程序读取该请求并进行处理,接下来介绍处理请求所作的工作。
2.3.6 代码片段4
struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
{
struct fuse_session_ops sop = {
}
2.3.7 代码片段5
static void fuse_ll_process(void *data, const char *buf, size_t len,
{
struct fuse_req *req;
req->unique = in->unique;
……
//根据opcode调用fuse_ll_ops中相应的方法
}
2.3.8 二阶段小结
以上代码对应中流程中perform unlink的工作,实际上就是执行开发者实现的一组方法来完成相关的工作,接下来就是把执行完请求后需要的数据返回,最终是通过send_reply实现的,
2.3.9 代码片段6
static int send_reply(fuse_req_t req, int error, const void *arg,
{
}
static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
{
}
static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
{
}
另外fuse接收到VFS的请求时,通过request_send将请求发送到/fuse/dev,并调用request_wait_answer等待返回结果。至于fuse使用的队列的管理,在流程图中也做了简单的说明,下一章将详细分析队列的管理。
2.3.10 代码片段7
void request_send(struct fuse_conn *fc, struct fuse_req *req)
{
}
2.4 队列管理
fuse通过fuse_session_loop来启动守护程序,守护程序最终会调用fuse_dev_readv,fuse_dev_readv调用request_wait,使得进程在fc的waitq队列上睡眠。
2.4.1 代码片段1
static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
{
}
static void request_wait(struct fuse_conn *fc)
{
//将wait加入到fc->waitq等待队列中,当有请求发到fuse文件系统时(通过request_send),这个等待队列上的进程会被唤醒,某一个进程会被赋予CPU使用权
}
static int request_pending(struct fuse_conn *fc)
{
}
2.4.2 代码片段2
request_send是用户请求经过vfs,再到fuse operation中被调用的,它向/dev/fuse发送请求
void request_send(struct fuse_conn *fc, struct fuse_req *req)
{
}
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
{
}
static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
{
}
fuse守护程序处理完请求,最终通过fuse_dev_writev写回/dev/fuse,它将唤醒相应req中waitq的等待队列元素,从而让文件系统请求完成request_wait_answer,获取到结果。
static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
{
}
static void request_end(struct fuse_conn *fc, struct fuse_req *req)
{
}
fuse设备其的主要工作其实就是进行队列的管理,对fuse设备的读(写)其实就是从相应的队列移除(添加)请求(或响应),request_send将请求加入pending队列,唤醒fuse守护程序,并在req的waitq上等待请求结果,守护程序通过fuse_dev_readv从pending队列中移除请求并处理,处理完成后,守护程序唤醒req的waitq上的进程,该进程读取结果,并返回给用户。总的来说,一个请求从发起到完成会经过4步:
ü
ü
ü
ü
ü
3 开发API
参见http://fuse.sourceforge.net/doxygen/index.html 或PPT《Building fs with FUSE》
4 参考资料
l
l
l
l
l
l
l
l
l
相关文章推荐:
This entry was posted on 2010/11/22, 21:41 and is filed under FUSE. You can follow any responses to this entry through RSS 2.0. You can leave a response, or trackback from your own site.
http://my.debugman.net/program/fuse-180.html