共计 3914 个字符,预计需要花费 10 分钟才能阅读完成。
本篇内容主要讲解“linux fuse 指的是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“linux fuse 指的是什么”吧!
Linux 用于支持用户空间文件系统的内核模块名叫 FUSE。fuse 全称“Filesystem in Userspace”,中文意思为“用户空间文件系统”,指完全在用户态实现的文件系统,是 Linux 中用于挂载某些网络空间,是一个通用操作系统重要的组成部分。
linux fuse 是什么
用户空间文件系统(Filesystem in Userspace),指完全在用户态实现的文件系统,是 Linux 中用于挂载某些网络空间,如 SSH,到本地文件系统的模块,在 SourceForge 上可以找到相关内容。
Linux 用于支持用户空间文件系统的内核模块名叫 FUSE,FUSE 一词有时特指 Linux 下的用户空间文件系统。是一个通用操作系统重要的组成部分。传统上操作系统在内核层面上对文件系统提供支持。而通常内核态的代码难以调试,生产率较低。
所谓“用户态文件系统”,是指一个文件系统的 data 和 metadata 都是由用户态的进程提供的(这种进程被称为 daemon)。对于 micro-kernel 的操作系统来说,在用户态实现文件系统不算什么,但对于 macro-kernel 的 Linux 来说,意义就有所不同。
虽然叫做用户态文件系统,但不代表其完全不需要内核的参与,因为在 Linux 中,对文件的访问都是统一通过 VFS 层提供的内核接口进行的(比如 open/read),因此当一个进程(称为 user)访问由 daemon 实现的文件系统时,依然需要途径 VFS。
当 VFS 接到 user 进程对文件的访问请求,并且判断出该文件是属于某个用户态文件系统(根据 mount type),就会将这个请求转交给一个名为 fuse 的内核模块。而后,fuse 将该请求转换为和 daemon 之间约定的协议格式,传送给 daemon 进程。
可见,在这个三方关系中,fuse 这个内核模块起的是一个转接的作用,它帮助建立了 VFS(也可以说是 user 进程)和 daemon 之间的交流通道,通俗点说,它的角色其实就是一个「代理」。
这一整套框架的实现在 Linux 中即为 FUSE (Filesystem in Userspace)。如图 1 所示,红框的部分才是 FUSE 类型文件系统的具体实现,才是用户态文件系统的设计者可以发挥的空间。目前,已有不下百种基于 FUSE 实现的文件系统(一些基于内核的文件系统也可以 porting 成用户态文件系统,比如 ZFS 和 NTFS),而本文将选用一个现成的 fuse-sshfs 来进行演示。
首先安装 fuse-sshfs 的软件包,使用如下的命令进行文件系统的 mount(将远端机器的 remote-dir 目录挂载到本机的 local-dir 目录):
sshfs remote-ip : remote-dir local-dir
之后,在 /sys/fs 目录下,将生成一个名为 fuse 的文件夹,同时可以看到 fuse 内核模块已被加载(其对应的设备为 /dev/fuse),并且本机的挂载目录的类型已成为 fuse.sshfs:
生成设备节点的目的是方便用户态的控制,但是对于文件系统这种级别的应用来说,直接使用 ioctl() 来访问设备还是显得麻烦,因为呈现了太多的细节,所以 libfuse 作为一个中间层应运而生,daemon 进程实际都是通过 libfuse 提供的接口来操作 fuse 设备文件的。
你来我往
接下来,以在 fuse.sshfs 文件系统中通过 touch 命令新建一个文件为例,查看 fuse 内核模块和 daemon 进程(即 sshfs)具体的交互流程(代码部分基于内核 5.2.0 版本):
【第一轮】
最开始是 permission 的校验,不过这里的校验并不等同于 VFS 的权限校验,它的主要目的是为了避免其他 user 访问到了自己私有的 fuse 文件系统。
然后就是根据文件路径查找文件的 inode。由于是新建的文件,inode 并不在内核的 inode cache 中,所以需要向 daemon 发送 lookup 的请求:
这些请求会被放入一个 pending queue 中,等待 daemon 进程的回复,而 user 进程将陷入睡眠:
作为 daemon,sshfs 进程通过读取 /dev/fuse 设备文件来获得数据,如果 pending queue 为空,它将陷入阻塞等待:
当 pending queue 上有请求到来时,daemon 进程将被唤醒并处理这些请求。被处理的请求会被移入 processing queue,待 daemon 进程向 fuse 内核模块做出 reply 之后,user 进程将被唤醒,对应的 request 将从 processing queue 移除。
【第二轮】
接下来就是执行 touch 命令时所触发的其他系统调用,如果是之前访问过的 data/metadata,那很可能存在于 cache 中,再次访问这部分 data/metadata 的时候,fuse 内核模块就可以自行解决,不需要去用户空间往返一趟,否则还是需要上报 daemon 进程进行处理。
这里 get_fuse_conn() 获取的是在 fuse 类型的文件系统被 mount 时创建的 fuse_conn 结构体实例。作为 daemon 进程和 kernel 联系的纽带,除非 daemon 进程消亡,或者对应的 fuse 文件系统被卸载,否则该 connection 将一直存在。
在 daemon 进程这一端,还是类似的操作。需要注意的是区别 fuse_write/read() 和 fuse_dev_write/read() 这两个系列的函数,前者是 user 进程在访问 fuse 文件系统上的文件时的 VFS 读写请求,属于对常规文件的操作,而后者是 daemon 进程对 /dev/fuse 这个代表 fuse 内核模块的设备的读写,目的是为了获取 request 和给出 reply。
【第三轮】
fuse 内核模块和 daemon 进程的最后一轮交互是在代表 fuse 文件系统的 superblock 中获取 inode 号,并填写这个 metadata 的相关信息。
硬币的两面
不难发现,在 fuse 文件系统中,即便执行一个相对简单的 touch 操作,所涉及的用户态和内核态的切换都是比较频繁的,并且还伴随着多次的数据拷贝。相比于传统的内核文件系统,它整体的 I / O 吞吐量更低,而延迟也更大。
那为什么 fuse 在操作系统支持的文件系统里面依然占据一席之地呢?说起来,在用户态开发是有很多优势的。一是便于调试,特别适合做一个新型文件系统 prototype 的快速验证,因此在学术研究领域颇受青睐。在内核里面,你只能用 C 语言吧,到了用户态,就没那么多限制了,各种函数库,各种编程语言,都可以上。
二是内核的 bug 往往一言不合就导致整个系统 crash(在虚拟化的应用中更为严重,因为宿主机的 crash 会导致其上面运行的所有虚拟机 crash),而用户态的 bug 所造成的影响相对有限一些。
所以,硬币的正面是便于开发,不过到底有多方便,这毕竟是一种主观的感受,而反面则是性能的影响,这可是能够用客观的实验数据来验证的。那应该用什么方法才能相对准确地衡量 fuse 所带来的损耗呢?
还是用前面用过的这个 fuse-sshfs,不过这里我们不再使用远端挂载,而是采用本地挂载的方式(假设本机的 dir-src 目录位于 ext4 文件系统):
sshfs localhost: dir-src dir-dst
当 daemon 进程收到请求后,它需要再次进入内核,去访问 ext4 的内核模块(这种文件系统模式被称为 stackable 的):
以 user 进程向 fuse 文件系统发出 write() 请求为例,右边红框部分是一次原生的 ext4 调用路径,而左边多出来的就是因为引入 fuse 后增加的路径:
根据这篇文档给出的数据,在这一系统调用中使用到的 getxattr 所形成的 request,需要 2 倍的 user-kernel 交互量。对于顺序写,相比起原生的 ext4 文件系统,I/ O 吞吐量降低 27%,随机写则降低 44%。
不过,在 fuse 文件系统诞生的这么多年里,大家还是为它想出了很多的优化举措。比如,顺序读写的时候,可以设计为向 daemon 进程批量发送 request 的形式(但随机读写不适合)。
还有就是使用 splicing 这种 zero-copy 技术,由 Linux 内核提供的 splicing 机制允许用户空间在转移两个内核的内存 buffer 的数据时,不需要拷贝,因此尤其适合 stackable 模式下,从 fuse 内核模块直接向 ext4 内核模块传递数据(但 splicing 通常用于超过 4K 的请求,小数据量的读写用不上)。
经过这些努力,fuse 文件系统的性能可以达到什么样的一种程度呢?根据这篇报告列出的测试结果,相比起原生的 ext4,在最理想的情况下,fuse 的性能损耗可以控制到 5% 以内,但最差的情况则是 83%。同时,其对 CPU 的资源占用也增加了 31%。
从 Android v4.4 到 v7.0 之间存在的 sdcard daemon,到最近几年的 Ceph 和 GlusterFS,都曾经采用过或正在采用基于 FUSE 的实现。FUSE 在 network filesystem 和虚拟化应用中都展现了自己的用武之地,它的出现和发展,并不是要取代在内核态实现的文件系统,而是作为一个有益的补充(理论上,FUSE 还可以用于实现根文件系统,但是不建议这么做,can do 和 should do 是两回事)。
到此,相信大家对“linux fuse 指的是什么”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!