在用户态实现文件系统——SSHFS、GlusterFS、CephFS FUSE client、rclone mount 都用 FUSE。代价是每次 I/O 多两次上下文切换。如何逼近内核文件系统性能?
一、先看图
sequenceDiagram
participant App as 用户进程
participant VFS as VFS
participant FUSE_K as FUSE 内核模块
participant FUSE_D as FUSE daemon<br/>(用户态 FS)
App->>VFS: read("/mnt/fuse/file")
VFS->>FUSE_K: fuse_file_read_iter()
FUSE_K->>FUSE_D: /dev/fuse → FUSE_READ 请求
FUSE_D->>FUSE_D: 处理(网络/本地)
FUSE_D->>FUSE_K: /dev/fuse → 写回数据
FUSE_K->>VFS: 返回数据
VFS->>App: read() 完成
二、FUSE 架构
2.1 三部分
- FUSE
内核模块(
fs/fuse/):注册 VFS 操作,转发请求到/dev/fuse - /dev/fuse:用户态 daemon 通过 read/write 与内核交互
- FUSE daemon:用户态进程,实现文件系统逻辑(libfuse 辅助)
2.2 协议
FUSE 请求/响应是结构化消息:
struct fuse_in_header {
uint32_t len;
uint32_t opcode; // FUSE_READ, FUSE_WRITE, FUSE_LOOKUP...
uint64_t unique; // 请求 ID
uint64_t nodeid; // inode 编号
uint32_t uid, gid, pid;
};大约 50 种 opcode 覆盖所有 VFS 操作。
三、性能瓶颈
每次 I/O:
- 用户进程 → 内核(syscall)
- 内核 → FUSE daemon(context switch + copy)
- FUSE daemon 处理
- FUSE daemon → 内核(write /dev/fuse)
- 内核 → 用户进程
两次额外上下文切换 + 两次数据拷贝。
3.1 benchmark 数据
典型场景(4KB 随机读):
- ext4:~150K IOPS
- FUSE passthrough(理想):~90K IOPS
- 普通 FUSE:~30-50K IOPS
吞吐下降 30%-70%。
四、优化手段
4.1 writeback cache
mount.fuse3 myfs /mnt -o writeback_cache启用页缓存的 writeback 模式 → 小写合并 → 减少到 daemon 的请求次数。
4.2 splice
FUSE 支持 splice 零拷贝传输:daemon 通过 splice 直接在 pipe buffer 和 /dev/fuse 之间移动数据。
4.3 multi-threaded daemon
libfuse 支持多线程处理请求。单线程 daemon 是性能反模式。
4.4 readahead
内核 FUSE 模块支持
readahead(max_readahead)→ 预读合并请求。
五、FUSE passthrough(6.9+)
FUSE_PASSTHROUGH:daemon 告诉内核”这个文件直接 I/O 到底层 fd”→ 后续读写完全在内核完成,不经过 daemon。
// daemon 在 FUSE_OPEN 响应里
out->passthrough.fd = backing_fd;
out->open_flags |= FOPEN_PASSTHROUGH;效果:接近原生文件系统性能。元数据操作还走 daemon,但数据路径完全旁路。
用例:Android FUSE(用户态控制权限,数据走 passthrough)。
六、virtio-fs
虚拟机场景:guest 访问 host 文件。传统 9p → 性能差。
virtio-fs = FUSE 协议 + virtio 传输 + DAX(直接映射 host 页缓存到 guest)。
flowchart LR
GUEST[Guest<br/>FUSE 内核模块] -->|virtio| VMM[virtiofsd<br/>Host daemon]
VMM --> HOST_FS[Host 文件系统]
GUEST -.->|DAX window| HOST_FS
classDef guest fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef host fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class GUEST guest
class VMM,HOST_FS host
DAX 模式:guest 直接 mmap host 页 → 零拷贝 + 共享页缓存。
七、io_uring FUSE(实验性)
传统 FUSE 数据路径:read/write /dev/fuse → syscall 开销。
io_uring FUSE(LSFMM 2023 讨论):把 FUSE 请求/响应放到 SQ/CQ ring → 减少 syscall 和上下文切换。尚未合入主线。
八、常见 FUSE 文件系统
| 项目 | 用途 |
|---|---|
| SSHFS | SSH 远程挂载 |
| rclone mount | 云存储挂载 |
| GlusterFS | 分布式存储 |
| CephFS FUSE | Ceph 用户态客户端 |
| NTFS-3G | NTFS 读写 |
| s3fs / goofys | S3 挂载 |
| juicefs | 云原生文件系统 |
九、开发 FUSE 文件系统
// libfuse3 最小示例
static int myfs_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi) {
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
return 0;
}
return -ENOENT;
}
static const struct fuse_operations myfs_ops = {
.getattr = myfs_getattr,
};
int main(int argc, char *argv[]) {
return fuse_main(argc, argv, &myfs_ops, NULL);
}Go: bazil.org/fuse、Rust:
fuser。
十、小结
- FUSE 把文件系统实现搬到用户态:开发灵活、安全隔离
- 代价是两次额外上下文切换 + 数据拷贝
- FUSE_PASSTHROUGH(6.9+)旁路数据路径 → 接近原生性能
- virtio-fs 用 FUSE + virtio + DAX 解决虚拟机文件共享
- io_uring FUSE 进一步减少 syscall → 未来方向
参考文献
Documentation/filesystems/fuse.rst- Miklos Szeredi, “FUSE: Filesystem in Userspace.”
- Vivek Goyal, “virtio-fs: shared file system for virtual machines.” KVM Forum 2019
fs/fuse/passthrough.c(6.9+)- LSFMM 2023, “io_uring for FUSE.”
工具
fusermount3 -u /mnt(卸载)ltrace -e fuse_*(跟踪 libfuse 调用)/sys/fs/fuse/connections/(查看连接状态)
上一篇:OverlayFS 下一篇:VFS I/O 路径
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】io_uring 内核内部
io_uring 用共享内存 ring buffer 实现零 syscall 异步 I/O——SQ/CQ、SQPOLL、IOPOLL、注册 fd/buffer、multishot、安全模型演化。本文深入内核实现与工程实践。
【操作系统百科】异步 I/O 模型 benchmark
epoll、io_uring、libaio、阻塞线程池——四种异步模型的真实性能对比。本文用统一 workload 量化 echo server、静态文件服务、数据库 I/O 场景下的吞吐、延迟与 CPU 开销。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。