FUSE fastpath特性说明和使用指南
介绍
FUSE(Filesystem in Userspace)允许非 root 用户在用户空间创建自己的文件系统,开发者不必修改内核代码,也无需内核模块开发经验,就能够快速实现新的、自定义的文件系统。FUSE 具有简化文件系统开发、扩展文件系统能力、提升安全性和稳定性的优点,但由于对 IO 性能的影响较大,其使用有所受限。为了提高 FUSE 的性能,openEuler 为 FUSE 提供了 fastpath 特性,该特性通过预创建线程绑核、共享内存、快速进程切换等功能实现了单线程和多线程下 FUSE 的加速。具体而言,有如下 3 项技术:
- 当挂载用户态文件系统时,就在每个 cpu 上都创建 1 个对应的 FUSE daemon,并将其绑定到对应的 CPU 上。当用户进程下发 IO 后,直接唤起同一 CPU 上的 FUSE daemon,这减少了创建和销毁线程的开销,同时可以尽可能利用 CPU 的 cache (用户进程和 FUSE daemon 通常会访问同样的内存)。每个线程绑核后相当于有了一个 percpu 的变量,不同 CPU 上的用户进程下发 IO 后不会互相竞争锁,提高了并行性。
- Fuse内核模块与FUSE daemon间创建一块共享内存,将FUSE的头信息直接放入到共享内存中,避免了寻址和复制部分的开销。
- 通过快速线程切换技术,可以在内核中显式地切换 CPU 上运行的线程,无需常规的线程切换流程,实现更高的 CPU 利用率。
使用方式
环境要求
- 硬件要求:ARM64架构处理器。
- 软件要求:目前 FUSE fastpath 特性仅在 openEuler 22.03 LTS SP4 上提供,且需要内核态和用户态的配合。使能 fastpath 特性要求内核最低版本为 5.10.0-264.0.0;用户态部分目前仅对 libfuse 进行了适配,要求 fuse3 软件包的最低版本为 3.10.5-11。开发者可以在 openEuler 的 update 源中获取到更新的软件包。
使用方法
动态库链接
fastpath 特性用户态部分以 fuse3 软件包中的 libfuse 动态库形式提供。对于使用 libfuse 动态库的用户态文件系统,链接时可以使用如下方式:
gcc program.o -L/path/to/lib -lfuse -o program
对于已经编译为二进制的用户态文件系统,开发者可以通过如下命令查看当前链接的动态库:
ldd /path/to/binary
若当前链接的动态库并非目标版本,可以使用如下方法:
方法一:更新
/etc/ld.so.conf
打开或创建一个新的配置文件,例如
/etc/d.so.conf.d/newlib.conf
,并添加库所在路径:bash/path/to/lib
运行以下命令更新缓存:
bashsudo ldconfig
方法二:设置 LD_LIBRARY_PATH
可以通过设置环境变量 LD_LIBRARY_PATH 来指定库的路径。这种方法不需要管理员权限,但只对当前会话有效。
bashexport LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
适配和使能
确定链接到正确版本的动态库后,开发者可以通过在创建 fuse session 时增加参数的方式使能 fastpath,具体而言,包括以下3个配置:
- use_fastpath:使能 fastpath,即上述的预创建线程绑核、共享内存、快速进程切换功能。
- no_interrupt:不处理 interrupt 请求。
- no_forget:不处理 forget 请求。
no_forget 和 no_interrupt 可以提升性能,但会造成部分功能缺失,需确定用户态文件系统不会使用这两类请求时再启用,否则可能造成问题。
需要注意的是,增加参数是指在使用 libfuse 中创建 fuse session 的函数 fuse_session_new 时传入的参数,而非使用挂载所需用户态文件系统的二进制时增加的参数,具体的适配方式因具体的用户态文件系统而异。以libfuse自带的演示demo ( passthrough_hp ) 为例,其代码需做如下适配:
diff --git a/example/passthrough_hp.cc b/example/passthrough_hp.cc
index 872fc73..1f96820 100644
--- a/example/passthrough_hp.cc
+++ b/example/passthrough_hp.cc
@@ -1146,7 +1146,10 @@ static cxxopts::ParseResult parse_options(int argc, char **argv) {
("help", "Print help")
("nocache", "Disable all caching")
("nosplice", "Do not use splice(2) to transfer data")
- ("single", "Run single-threaded");
+ ("single", "Run single-threaded")
+ ("nointerrupt", "Do not process interrupt request")
+ ("noforget", "Do not process forget request")
+ ("usefastpath", "use fastpath");
// FIXME: Find a better way to limit the try clause to just
// opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146)
@@ -1225,7 +1228,10 @@ int main(int argc, char *argv[]) {
if (fuse_opt_add_arg(&args, argv[0]) ||
fuse_opt_add_arg(&args, "-o") ||
fuse_opt_add_arg(&args, "default_permissions,fsname=hpps") ||
- (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")))
+ (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")) ||
+ (options.count("nointerrupt") && fuse_opt_add_arg(&args, "-ono_interrupt")) ||
+ (options.count("noforget") && fuse_opt_add_arg(&args, "-ono_forget")) ||
+ (options.count("usefastpath") && fuse_opt_add_arg(&args, "-ouse_fastpath")))
errx(3, "ERROR: Out of memory");
fuse_lowlevel_ops sfs_oper {};
挂载方式:
passthrough_hp --usefastpath /path/to/src /path/to/mnt
参数 --usefastpath
进入 passthrough_hp 后会被解析为 -ouse_fastpath
参数,用于创建新的 fuse session,带有该参数的 session 后续在挂载和创建 FUSE daemon 时会使能fastpath。