kfs 是一个基于 FUSE 的类 ext4 文件系统, 同时可以利用 Virtio(vhost-user) 协议实现虚拟机(VM)和宿主机之间高效共享文件系统.
本项目共包含四个部分
kfs
: 文件系统, 可以挂载磁盘镜像并映射文件系统mkfs
: 磁盘格式化工具, 可以将磁盘/文件格式化为 kfs 文件系统需要的格式kfsd
: 通过 Virtio(vhost-user) 协议实现虚拟机(VM)和宿主机之间通信, 将 VM 请求转发至 host 文件系统处理kfsctl
: 命令行交互工具, 可以查看挂载的文件系统状态, 快照, 恢复, 磁盘碎片整理等操作
安装依赖
sudo apt-get install fuse3 libfuse3-dev pkg-config libcap-ng-dev libseccomp-dev
编译得到文件系统 src/kfs
和磁盘格式化程序 mkfs/mkfs
和命令行交互工具 kfsctl/kfsctl
make
cd kfsd
cargo build --release
创建 disk.img
文件(1000 MiB), 并使用 mkfs/mkfs
将其格式化为 ext4 文件系统格式
make disk
挂载文件系统到 tmp/
下
mkdir tmp
make run
可以进入 tmp/
目录下进行操作, 例如创建/打开/读取/修改文件等等, 所有修改都会保存在 disk.img
中, 所有的操作日志保存在 kfs.log
中
结束操作后使用取消挂载文件系统即可
umount tmp
或者以调试模式挂载并执行
make debug_run
测试
make test
Guest kernel 编译时需要添加 virtiofs 支持
CONFIG_VIRTIO_FS
CONFIG_FUSE
启动 virtfsd
cargo run -- --shared-dir <dir> --socket-path /tmp/vhostqemu
qemu 启动时添加 chardev
用于创建一个 socket(/tmp/vhostqemu) 实现通信. 创建一个 vhost-user-fs-pci
的 device, 添加 tag 为 myfs 用于后续挂载
qemu-system-x86_64 \
-kernel <your-kernel> \
-drive <your-drive>
-chardev socket,id=char0,path=/tmp/vhostqemu \
-device vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=myfs \
-m 4G -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \
-numa node,memdev=mem
sudo mount -t virtiofs myfs <mountpoint>
项目技术文档见 md-docs/
, 在线阅读: kfs document
关于细节见 impl 部分
在远程文件系统中,**transport(传输)和protocol(协议)**是两个核心概念,它们共同决定了数据如何在客户端和服务器之间传输,以及如何进行通信和交互.
- Transport 指数据在网络上传输的方式和路径.在远程文件系统中,传输层负责确保数据包能够在客户端和服务器之间可靠地传递.选择合适的传输协议取决于系统的需求,例如: TCP/IP, USB, RDMA, 通过消息传递或者共享内存等方式
- Protocol 指在客户端和服务器之间进行通信的规则和格式.它定义了消息的结构、命令的含义、错误处理机制等. 例如 NFS, CIFS, SSH 等
那么虚拟化场景下有什么特别的么? 注意到所有虚拟机共用同一个物理机, 他们的物理内存实际上是共享的, 由 VMM 统一管理和分配, 也就是不需要高开销的网络通信而可以通过一种高效的内存映射的方式完成通信
最知名的虚拟化平台 QEMU 采用 vhost-user 协议. vhost-user 是一种用于用户态进程与内核态虚拟化组件(如KVM/QEMU)之间进行高速数据传输的通信协议.它最常见的用途是在虚拟机和虚拟设备(如虚拟网络接口、虚拟磁盘)之间实现高效的I/O操作
通常由 qemu 的 virtio-net 的实现虚拟网络通信设备作为 vhost-user 协议的 frontend 部分, 本项目实现了 vhost-user 协议的后端(backend) 部分, 与 VM 通信握手
传统 FUSE 的访问流程如下
- FUSE_INIT to creat session
- FUSE_LOOPUP(FUSE_ROOT_ID, "foo") -> nodeid
- FUSE_OPEN(nodeid, O_RDONLY) -> fh
- FUSE_READ(fh, offset, &buf, sizeof(buf)) -> nbytes
能否避免/减少与 kfsd 的通信开销? 能否避免与主机频繁的拷贝数据 from/to, DAX
(Direct Access) 是virtiofs中的一项重要特性. 可以将数据可以直接从存储设备映射到用户空间文件区域
- 将文件区域映射到 guest 的内存区域
- 允许 guest mmap 访问内存区域
启用 DAX 之后可以充分利用主机的 page cache, pte 等加速访存操作, 避免通信和数据拷贝.
- FUSE_INIT to creat session
- FUSE_LOOPUP(FUSE_ROOT_ID, "foo") -> nodeid
- FUSE_OPEN(nodeid, O_RDONLY) -> fh
- FUSE_SETUPMAPPING(fh, offset, len, addr)
- Memory access to [addr, addr+len]