用户态文件系统 - FUSE

2024-04-12 02:08
文章标签 用户 文件系统 fuse

本文主要是介绍用户态文件系统 - FUSE,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

用户态文件系统 - FUSE

 

所谓“用户态文件系统”,是指一个文件系统的data和metadata都是由用户态的进程提供的(这种进程被称为"daemon")。对于micro-kernel的操作系统来说,在用户态实现文件系统不算什么,但对于macro-kernel的Linux来说,意义就有所不同。

虽然叫做用户态文件系统,但不代表其完全不需要内核的参与,因为在Linux中,对文件的访问都是统一通过VFS层提供的内核接口进行的(比如open/read),因此当一个进程(称为"user")访问由daemon实现的文件系统时,依然需要途径VFS。

当VFS接到user进程对文件的访问请求,并且判断出该文件是属于某个用户态文件系统(根据mount type),就会将这个请求转交给一个名为"fuse"的内核模块。而后,"fuse"将该请求转换为和daemon之间约定的协议格式,传送给daemon进程。

图 1

可见,在这个三方关系中,"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"是两回事)。

https://zhuanlan.zhihu.com/p/143256077

这篇关于用户态文件系统 - FUSE的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/895813

相关文章

Mysql中的用户管理实践

《Mysql中的用户管理实践》:本文主要介绍Mysql中的用户管理实践,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录13. 用户管理13.1 用户 13.1.1 用户信息 13.1.2 创建用户 13.1.3 删除用户 13.1.4 修改用户

详解如何在SpringBoot控制器中处理用户数据

《详解如何在SpringBoot控制器中处理用户数据》在SpringBoot应用开发中,控制器(Controller)扮演着至关重要的角色,它负责接收用户请求、处理数据并返回响应,本文将深入浅出地讲解... 目录一、获取请求参数1.1 获取查询参数1.2 获取路径参数二、处理表单提交2.1 处理表单数据三、

什么是ReFS 文件系统? ntfs和refs的优缺点区别介绍

《什么是ReFS文件系统?ntfs和refs的优缺点区别介绍》最近有用户在Win11Insider的安装界面中发现,可以使用ReFS来格式化硬盘,这是不是意味着,ReFS有望在未来成为W... 数十年以来,Windows 系统一直将 NTFS 作为「内置硬盘」的默认文件系统。不过近些年来,微软还在研发一款名

CentOS和Ubuntu系统使用shell脚本创建用户和设置密码

《CentOS和Ubuntu系统使用shell脚本创建用户和设置密码》在Linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设置密码,本文写了一个shell... 在linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设

SpringBoot UserAgentUtils获取用户浏览器的用法

《SpringBootUserAgentUtils获取用户浏览器的用法》UserAgentUtils是于处理用户代理(User-Agent)字符串的工具类,一般用于解析和处理浏览器、操作系统以及设备... 目录介绍效果图依赖封装客户端工具封装IP工具实体类获取设备信息入库介绍UserAgentUtils

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

Linux中的缓冲区和文件系统详解

《Linux中的缓冲区和文件系统详解》:本文主要介绍Linux中的缓冲区和文件系统方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、FILE结构1、fd2、缓冲区二、文件系统1、固态硬盘2、逻辑地址LBA(一)数据块 Data blocks(二)inode表

mysql删除无用用户的方法实现

《mysql删除无用用户的方法实现》本文主要介绍了mysql删除无用用户的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 1、删除不用的账户(1) 查看当前已存在账户mysql> select user,host,pa

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Oracle数据库如何切换登录用户(system和sys)

《Oracle数据库如何切换登录用户(system和sys)》文章介绍了如何使用SQL*Plus工具登录Oracle数据库的system用户,包括打开登录入口、输入用户名和口令、以及切换到sys用户的... 目录打开登录入口登录system用户总结打开登录入口win+R打开运行对话框,输php入:sqlp