Linux 文件系统中,例如 ext4、xfs、btrfs 等,文件系统逻辑通常运行在 内核空间(Kernel Space),所以文件系统的开发必须编写 内核模块,需要深入理解 VFS、页缓存、块设备等复杂机制,一旦代码出现问题,极易导致 Kernel Panic。
为了解决这问题,Linux 提供了 FUSE(Filesystem in Userspace)。
FUSE 允许开发者在 用户态实现文件系统逻辑,内核只提供一个桥接模块,将文件系统请求转发给用户空间程序处理。
这使得开发文件系统变得更加简单、安全、灵活。
我们从 Linux 内核架构角度,深入解析:
FUSE 的整体架构
VFS 与 FUSE 的关系
FUSE 内核模块工作机制
用户态文件系统实现方式
请求处理流程
读写路径源码解析
性能优化机制
实际应用案例
本文适合 Linux 内核 / 系统开发 / 存储开发工程师。
一、为什么需要 FUSE
在 Linux 早期,如果要开发文件系统,一般有两种方式:
但开发 内核文件系统 有明显缺点:
1 内核开发复杂
文件系统需要实现:
inode
dentry
superblock
page cache
block IO
journaling
代码量往往 几十万行。
2 调试困难
如果文件系统存在 bug:
可能直接导致:
3 安全风险
内核模块拥有最高权限。
一个 bug 可能:
因此 Linux 引入 FUSE(Filesystem in Userspace):
核心思想:
文件系统逻辑放在用户态内核只负责请求转发
优点:
二、FUSE 整体架构
FUSE 的核心由 三个组件组成:
+---------------------------+| User Space || || FUSE Filesystem Daemon || (sshfs / ntfs-3g) |+-------------▲-------------+ | /dev/fuse |+-------------▼-------------+| Kernel Space || || fuse.ko || (FUSE driver) |+-------------▲-------------+ | VFS | Applications
Application │ ▼ VFS │ ▼ fuse.ko │ ▼ /dev/fuse │ ▼FUSE Userspace FS
1 应用调用:
open()read()write()
2 VFS 调用 FUSE 文件系统
3 fuse 内核模块封装请求
4 请求通过 /dev/fuse 发送到用户态
5 用户态 daemon 处理请求
6 返回结果
三、Linux VFS 与 FUSE 的关系
在 Linux 中,所有文件系统统一由 VFS(Virtual File System) 管理。
VFS 提供统一接口:
open()read()write()mkdir()unlink()
不同文件系统实现:
struct file_operationsstruct inode_operations
FUSE 也实现了这些接口。
FUSE 在 VFS 中的位置
+------+ | VFS | +--▲---+ | +----------+-----------+ | | ext4 FUSE (kernel fs) (userspace fs)
VFS 并不知道:文件系统是 ext4 还是 FUSE,VFS 只调用统一接口。
四、FUSE 内核模块架构
FUSE 内核模块源码位于:
主要文件:
核心数据结构 struct fuse_conn
表示一个 FUSE 连接:
struct fuse_conn { struct list_head pending; struct list_head processing; struct list_head io;};
表示:
FUSE 请求结构 struct fuse_req
核心字段:
struct fuse_req { struct fuse_in_header in; struct fuse_out_header out;};
请求头
struct fuse_in_header { u32 len; u32 opcode; u64 unique; u64 nodeid;};
字段含义:
五、FUSE 挂载流程
用户挂载 FUSE 文件系统:sshfs user@host:/ /mnt
内部流程:
第一步
创建 /dev/fuse 连接:open("/dev/fuse")
第二步
调用 mount:mount -t fuse
第三步
内核创建 fuse_conn:fuse_conn_init()
第四步
建立用户态通信通道:/dev/fuse
六、FUSE 请求处理流程
以 read() 为例。
完整流程:
Application | | read() ▼VFS | ▼fuse_file_read_iter() | ▼fuse_simple_request() | ▼request queue | ▼/dev/fuse | ▼Userspace daemon | ▼filesystem logic | ▼return data
时序图是这样
App Kernel FUSE Daemon | | | | read() | | |---------------->| | | | fuse_read() | | |------------------->| | | | | | read file | | |<-------------------| | data | | |<----------------| |
七、FUSE 读操作源码流程
内核入口:fuse_file_read_iter()
ssize_tfuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to){ struct fuse_req *req; req = fuse_get_req(); fuse_read_fill(req); fuse_simple_request(fc, req); copy_to_user(...);}
关键步骤:
1 创建 request2 填充 read 参数3 发送到 userspace4 等待返回
请求发送 fuse_simple_request()
核心逻辑:
queue_request(fc, req);wake_up(&fc->waitq);
表示:将请求加入队列和唤醒用户态
八、/dev/fuse 通信机制
FUSE 通过字符设备:/dev/fuse 实现通信。
用户态 daemon:
read(fd)write(fd)
内核模块:
fuse_dev_read()fuse_dev_write()
读取请求
用户态:read(/dev/fuse)
内核:fuse_dev_read()
核心逻辑:req = dequeue_request(); copy_to_user(req)
写回响应
用户态:write(/dev/fuse)
内核:fuse_dev_write()
处理:complete_request()
九、用户态文件系统实现
用户态一般使用:libfuse提供接口。
例如:struct fuse_operations
定义文件系统操作:
static struct fuse_operations ops = { .getattr = my_getattr, .readdir = my_readdir, .read = my_read,};
举例代码
staticintmy_getattr(constchar *path, struct stat *stbuf){ memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } return 0;}
启动:fuse_main(argc, argv, &ops, NULL);
十、FUSE 目录操作流程
目录读取:ls
流程:
ls | VVFS readdir | Vfuse_readdir | Vuserspace readdir | V返回目录项
用户态实现:
staticintmy_readdir(...){ filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); filler(buf, "file.txt", NULL, 0);}
十一、FUSE 缓存机制
为了减少用户态切换开销,FUSE 提供缓存:Page Cache
默认:启用 page cache
流程:
read() |page cache hit |不需要用户态
十二、FUSE 实际应用
很多流行文件系统基于 FUSE:
sshfs 架构
十三、FUSE 与内核文件系统对比
十四、总结
FUSE 的核心思想非常简单:
内核负责转发用户态负责逻辑
完整架构:
优势:
缺点:性能略低
但对于很多场景(云存储、网络文件系统、加密文件系统)来说,FUSE 提供了一种 极其优雅的实现方式。