一 Fu简要介绍
1 什么是Fu
传统的文件系统是操作系统的一部分,放在操作系统内核里面实现。Fu(Filesystem in Urspace), 一个用户空间文件系统框架,提供给我们一组用于实现一个文件系统的API,使我们可以在用户态实现自已的文件系统。目前fu已集成在Linux2.6以上版本的内核中。
(注:操作系统中的用户态指权限等级中的一般级别,与之相对的是超级用户或者管理员的特权级别。用户态启动的每个进程,根据运行该进程的登录用户,都被系统赋予一定的权限,另外也有一些限制。)
2 优缺点
aggression1)传统文件系统都是定义在操作系统内核层面上的,要操作系统识别一种新的文件系统,必需重写内核,而内核态代码难以调试,生产率较低;但是用户空间编程和调试难度较小,有更多的语言可以选择(目前FUSE已经绑定了很多语言,比如c++、java等),还可以复用已有的库),从而能够大幅提高生产率,极大地简少了为操作系统提供新的文件系统的工作
量。
2)一些服务可以通过统一的文件系统接口来进行访问,比如说ftp、sftp、samba
3)可以把非文件的服务当做文件来实现,比如把gmail提供的巨大的空间用来进行文件存储的Gmail Filesystem。
在用户态实现文件系统必然会引入额外的内核态/用户态切换带来的开销,对性能会产生一定影响。
二 Fu的结构
flavorfu包括三个模块:用户空间库,内核模块以及mount工具
1)用户空间库给程序员提供编程接口,程序员通过实现fu提供的两组接口fu_lowlevel_ops, fu_operations之一即可实现一个用户空间文件系统
2)内核模块实现了一个完整文件系统的框架,但具体操作没有实现(由程序员在用户空间实现)
3)mount工具furmount用于挂载基于fu的文件系统
1 Fu在用户空间工作的流程图
通过这幅图可以看到三个模块在fu工作时所起的作用
fu_main() (lib/helper.c)——fu用户空间主函数,用户程序调用它时,fu_main()函数解析相关参数(如mountpoint,multithreaded),并调用fu_mount()函数,接着调用fu_new()函数,为fu文件系统数据分配存储空间。最后调用fu_loop()函数实现会话的接受与处理。
fu_mount() (lib/mount.c)——创建UNIX本地套接口,创建并运行子进程furmount。
furmount (util/furmount.c)——确保fu模块已经加载,通过UNIX套接口返回fu模块的文件fd给fu_mount()函数。
fu_new() (lib/fu.c)——为fu创建数据结构空间,用来存储文件系统数据。
fu_loop() (lib/fu.c)( fu_loop_mt() (lib/fu_mt.c))——从/dev/fu (/dev 设备文件存储目录)读取文件系统调用,调用fu_operations或fu_lowlevel_ops结构中的处理函数,返回调用结果给/dev/fu
2 Fu内核模块
implyFUSE Kernel模块由两部分组成:
第一部分——proc文件系统组件:Kernel/dev.c——回应io请求到/dev/fu。fu_dev_read()函数负责读出文件,并将来自“list of request”结构体的命令返回到调用函数。fu_dev_write ()负责文件写入,并将写入的数据置放到“req→out”数据结构中。
第二部分——文件系统调用部分:kernel/file.c,kernel/inode.c,kernel/dir.c——调用request_nd(),将请求加入到“list of request”结构体中,等待回复(reply)。
三 Fu调用流程
由于fu处理请求过程涉及的内容较多,如果从采用从外到内逐层深入的方法来讲,虽然符合逻辑但会增加理解难度,因为到最后大家会迷失在一个个的函数调用里,而且也难以抓住其本质与核心。所以我由其核心——队列管理讲起,向外扩散;再从最外层的函数调用向内讲;最后瞻前顾后,整个fu处理请求的流程就明白了。
我们先利用下面一幅图简要了解下fu文件系统工作时的调用路径。mail yahoo co jp
在shell里输入命令,请求通过vfs到达fu,然后通过用户实现的fu给出的API返回调用。
四级考试2020年考试时间
stance1 Fu处理请求的核心工作就是进行队列管理
1)两个重要的数据结构
fc的定义如下
/* A Fu connection.
* This structure is created, when the filesystem is mounted, and is destroyed, when the
* client device is clod and the filesystem is unmounted.
*/
Struct fu_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 procesd */
struct list_head processing; // 正在被处理的请求的 队列
/** Pending interrupts */
struct list_head interrupts; // 执行中被中断的请求的 队列
...
}
req的定义如下:
/*
*A request to the client英文字母顺序
enchanted */
struct fu_req
{
/** Ud to wake up the task waiting for completion of request*/
wait_queue_head_t waitq; // 请求的等待队列
championships…
}
broach
2)队列管理的过程如下
3)队列管理的相关代码
(左列一至五行) fu通过fu_ssion_loop来启动守护程序,守护程序最终会调用fu_dev_readv, fu_dev_readv调用request_wait,使得进程在fc的waitq队列上睡眠。
Static size_t fu_dev_readv(struct file *file, const struct iovec *iov, unsigned long nr_gs, loff_t *off)
{
….
request_wait(fc);
….
}
/* Wait until a request is available on the pending list
*当前进程一直等待,直到挂起队列中有一个请求
*/
static void request_wait(struct fu_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))
{
t_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) break;
spin_unlock(&fc->lock);
schedule(); //选择一个进程运行
spin_lock(&fc->lock);
}
// 有请求,将进程设为TASK_RUNNING状态(被唤醒,被赋予CPU使用权)
t_current_state(TASK_RUNNING);
remove_wait_queue(&fc->waitq, &wait); // 将wait(当前进程)从等待队列中移除
}
// fc的pending队列及interrupts队列,看是否有请求
static int request_pending(struct fu_conn *fc)
{
return !list_empty(&fc->pending) || !list_empty(&fc->interrupts);
}
(右列一到四)request_nd是用户请求经过vfs(如上面的图),再到fu operation中被调用的,它向/dev/fu发送请求
void request_nd(struct fu_conn *fc, struct fu_req *req)
{
……
queue_request(fc, req);
request_wait_answer(fc, req);
……
}
static void queue_request(struct fu_conn *fc, struct fu_req *req)