用户态文件系统 - 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

相关文章

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

数据库oracle用户密码过期查询及解决方案

《数据库oracle用户密码过期查询及解决方案》:本文主要介绍如何处理ORACLE数据库用户密码过期和修改密码期限的问题,包括创建用户、赋予权限、修改密码、解锁用户和设置密码期限,文中通过代码介绍... 目录前言一、创建用户、赋予权限、修改密码、解锁用户和设置期限二、查询用户密码期限和过期后的修改1.查询用

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

【Kubernetes】K8s 的安全框架和用户认证

K8s 的安全框架和用户认证 1.Kubernetes 的安全框架1.1 认证:Authentication1.2 鉴权:Authorization1.3 准入控制:Admission Control 2.Kubernetes 的用户认证2.1 Kubernetes 的用户认证方式2.2 配置 Kubernetes 集群使用密码认证 Kubernetes 作为一个分布式的虚拟

vue2实践:el-table实现由用户自己控制行数的动态表格

需求 项目中需要提供一个动态表单,如图: 当我点击添加时,便添加一行;点击右边的删除时,便删除这一行。 至少要有一行数据,但是没有上限。 思路 这种每一行的数据固定,但是不定行数的,很容易想到使用el-table来实现,它可以循环读取:data所绑定的数组,来生成行数据,不同的是: 1、table里面的每一个cell,需要放置一个input来支持用户编辑。 2、最后一列放置两个b

家庭和学生用户笔记本电脑配置方案

2.6.1  家庭和学生用户笔记本电脑配置方案   2.6.1  家庭和学生用户笔记本电脑配置方案   普通家庭用户、学生用户主要用于上网、娱乐、学习等,这类用户要求笔记本电脑的各方面 功能比较均衡。在选购此类笔记本电脑时,主要考虑外观设计方面要比较时尚,而且性能上也要 够强,一些大型复杂的软件以及目前的主流游戏都要能够流畅地运行才行。   对于CPU方面,可以考虑目前主流的第二

使用jetty和mongodb做个简易文件系统

使用jetty和mongodb做个简易文件系统 - ciaos 时间 2014-03-09 21:21:00   博客园-所有随笔区 原文   http://www.cnblogs.com/ciaos/p/3590662.html 主题  MongoDB  Jetty  文件系统 依赖库: 1,jetty(提供http方式接口) 2,mongodb的java驱动(访问mo