FUSE文件系统

Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持,开发者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文件系统。由于其主要实现代码位于用户空间中,而不需要重新编译内核,这给开发者带来了众多便利。Google在Android 11上,为了实现scoped storage,也引入了fuse。下面我们从Fuse的架构设计以及具体的实现细节来谈一谈fuse文件系统。

一、 Fuse架构设计

图片摘自《To FUSE or Not to FUSE: Performance of User-Space File Systems》

Fuse包含一个内核模块和一个用户空间守护进程(下文称fuse daemon)。内核模块加载时被注册成 Linux 虚拟文件系统的一个 fuse 文件系统驱动。此外,还注册了一个/dev/fuse的块设备。该块设备作为fuse daemon与内核通信的桥梁,fuse daemon通过/dev/fuse读取fuse request,处理后将reply写入/dev/fuse。

上图详细展示了fuse的构架。当application挂在fuse文件系统上,并且执行一些系统调用时,VFS会将这些操作路由至fuse driver,fuse driver创建了一个fuse request结构体,并把request保存在请求队列中。此时,执行操作的进程会被阻塞,同时fuse daemon通过读取/dev/fuse将request从内核队列中取出,并且提交操作到底层文件系统中(例如 EXT4 或 F2FS)。当处理完请求后,fuse daemon会将reply写回/dev/fuse,fuse driver此时把requset标记为completed,最终唤醒用户进程。

二、 Fuse实现细节

下面我们基于Android 11 AOSP 以及 kernel4.19的开源代码,讨论一些fuse的实现细节,包括:fuse 用户空间流程、内核队列、/dev/fuse的读写流程等。

1. fuse用户空间流程

(1) fuse mount

Fuse的挂载通过mount函数,将指定的fuse_path挂载到/dev/fuse设备上。之后对于fuse_path下的文件操作,都会通过fuse文件系统,并通过/dev/fuse被fuse daemon读取处理。

(2) fuse thread

Fuse  daemon还会创建一个服务线程,基于libfuse库来处理文件操作请求。这里主要关注fuse_session_new和fuse_session_loop_mt。通过fuse_session_new在libfuse中注册了fuse daemon实现的fuse_lowlevel_ops,之后通过fuse的所有的文件操作,都会通过libfuse回调到fuse daemon进行处理。

fuse_session_loop_mt在libfuse中实现了一个多线程模式来读取请求,相比单线程,在请求处理上效率更高。

(3) libfuse

由fuse_session_loop_mt在libfuse中的调用流程如下:

这里我们关注两点:

a) splice实现内存零拷贝。在默认情况下,fuse daemon必须通过read()从/dev/fuse读取请求,通过write()将请求回复写入/dev/fuse。每次读写系统调用都需要进行一次内核-用户空间的内存拷贝。这样对读写的性能损耗十分严重,因为一次内存拷贝需要处理大量数据。为了缓解这个问题,fuse支持了Linux内核提供的 splice 功能。splice 允许用户空间在两个内核内存缓冲区之间传输数据,而无需将数据复制给用户空间。如果fuse daemon实现了write_buf()方法,则 FUSE 从/dev/fuse读取数据,并以包含文件描述符的缓冲区的形式将数据直接传递给此方法处理,从而省去了一次内存申请与拷贝。

b) 多线程模式。在多线程模式下,fuse  daemon以一个线程开始,如果内核队列中有两个以上的request,则会自动生成其他线程。默认最大支持10个线程同时处理请求。

2. fuse内核队列

图片摘自《To FUSE or Not to FUSE: Performance of User-Space File Systems》

 

fuse在内核中维护了五个队列,分别为:Backgroud、Pending、Processing、Interrupts、Forgets。一个请求在任何时候只会存在于一个队列中。

a) Backgroud:background 队列用于暂存异步请求。在默认情况下,只有读请求进入 background 队列;当writeback cache启用时,写请求也会进入 background 队列。当开启writeback cache时,来自用户进程的写请求会先在页缓存中累积,然后当bdflush 线程被唤醒时会下刷脏页。在下刷脏页时,FUSE会构造异步请求,并将它们放入 background 队列中。

b) Pending:同步请求(例如,元数据)放在 pending 队列中,并且pending队列会周期性接收来自background 的请求。但是pending队列中异步请求的个数最大为max_background(最大为12),当pending队列的异步请求未达到12时,background队列的请求将被移动到pending队列中。这样做的目的是为了控制pending队列中异步请求的个数,防止在突发大量异步请求的情况下,阻塞了同步请求。

c) Processing:当pending队列中的请求被转发到fuse daemon的同时,也被移动到processing队列。所以processing队列中的请求,表示正在被处理fuse daemon处理的请求。当fuse daemon真正处理完请求,通过/dev/fuse下发reply时,该请求将从processing队列中删除。

d) Interrupts:用于存放中断请求,比如当发送的请求被用户取消时,内核会发送一个Interrupts请求,来取消已被发送的请求。中断请求的优先级最高,Interrupts中的请求会最先得到处理。

e) Forgets:forget请求用于删除dcache中缓存的inode。

 

3. /dev/fuse 读写调用流程

Fuse driver加载过程中注册了对/dev/fuse的操作接口fuse_dev_operations。fuse_dev_do_read/fuse_dev_do_write分别对应fuse daemon从内核读取请求,以及处理完请求后写回reply的函数调用。我们分别看下具体的代码片段

当pending 、interrups、forgets队列都没有请求时,读进程进入休眠。一旦有请求到达,这个等待队列上的进程将被唤醒。Interrups 和 forgets的请求优先级高于pending队列。当请求的数据内容被拷贝至用户空间后,该请求会被移至processing队列,并且req->flags会保存当前请求的状态。

当fuse daemon处理完请求后,会将结果写回到/dev/fuse。写数据保存在struct  fuse_copy_state中,并且会根据unique id在fc(fuse_conn)中找到对应的req,并将写回的参数从fuse_copy_state拷贝至req->out。

 

最后我们以unlink为例,看下fuse整体是如何工作的:

图片摘自fuse内核官方文档

 

首先,fuse daemon会阻塞在读/dev/fuse,当app进程在fuse挂载点下面有新的文件操作(unlink),这时系统调用会调用fuse内核接口,并生成request,同时唤醒阻塞的fuse daemon。fuse daemon读到request后,在libfuse中进行解析,根据request的opcode来执行对应的ops,完成后会把处理结果返回给/dev/fuse。此时vfs调用阻塞的行为将被唤醒,最后返回vfs调用。

三、 总结

虽然Fuse简化了文件系统的实现,给开发者带来了便利。但是其额外的内核态/用户态切换带来的性能开销不能被忽视,所以fuse性能问题,一直是业界绕不开的话题。前面说到的splice、多线程、writeback cache都是为了改善其性能问题。后续,我们再具体谈谈fuse性能改善。

 

参考文献:

[1] Bharath Kumar Reddy Vangoor, Vasily Tarasov, Erez Zadok.To FUSE or Not to FUSE: Performance of User-Space File Systems. in Proceedings of the 15th USENIX Conference on File and Storage Technologies (FAST ’17), 2017 • Santa Clara, CA, USA

    推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

myeclipse试用小记----Hibernate多对一双向关联(2)

myeclipse试用小记----Hibernate多对一双向关联(2)在上篇文章“myeclipse试用小记----Hibernate多对一单向关联(1)”中,讲到了“Hibernate多对一单向关联”,现在我打算把这个做成双向的,也就是多…

Python的程序结构[4] - 函数/Function[2] - 匿名函数

匿名函数 / Anonymous Function 匿名函数是一种不需要绑定函数名的函数 (i.e. functions that are not bound to a name)。匿名函数通过 lambda 表达式进行构建。常用于快速建立起一个(一次性的)函数。 Note: lambda 是 Python 的一个表达式/关键字,类似 return&…

数组 的地址计算

数组是一个特殊的数据结构,数组的基本操作不涉及数组结构的变化,因此对于数组而言,采用顺序存储表示比较合适。数组的顺序存储结构有两种:一、以行序存储,如高级语言BASIC、COBOL、Pascal、c语言。二、以列序存储&…

你会选择深圳还是佛山?

最近是校招季节,有很多人在询问offer的问题,我知道我已经发了很多这样相关的文章,可能大家看着都有点不耐烦了,不过还是想说,人生重要的选择真的没有几个,我每次回答都特别慎重,我有时候发出来也…

盗版XP变正版

此帖为转贴!经本人测试100%成功,谨以此帖献给各位目前不能更新的猫们,免得四处搜寻,徒劳无功!一、经过无数次验证,本方法绝对有效!敬请一试!!1。点击开始栏→…

区间K大数查询

给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。输入格式第一行包含一个数n,表示序列长度。第二行包含n个正整数,表示给定的序列。第三个包含一个正整数m,表示询问个数。接下来m行,每行三个数l,r,K&a…

optimizer

在很多机器学习和深度学习的应用中,我们发现用的最多的优化器是 Adam,为什么呢? 下面是 TensorFlow 中的优化器, https://www.tensorflow.org/api_guides/python/train 在 keras 中也有 SGD,RMSprop,Adagr…

【漫画】25岁程序员 VS 35岁程序员,塑造自己的不可替代性,才能让自己更有价值 ​...

其中的酸甜苦辣你中了几条经常有人说:35岁是程序员的魔咒。但其实相比于刚毕业的年轻人,虽然35岁的程序员从精力上和年龄上都不再占有优势,但十几年的沉淀所造就的从容也是这个年龄段所独有的。当然,也不只是程序员,任…

C语言——内存管理

C语言——内存管理 宗旨:技术的学习是有限的,分享的精神是无限的。 //变量的存储布局(伴随初级程序员和高级工程师一生的) 静态区:全局变量和static变量,初始化的全局变量和静态变量放在一块区域&#xf…

WIN命令

azman.msc--授权管理器admgmt.msc--ad管理calc-----------启动计算器certmgr.msc--证书-当前用户certtmpl.msc--证书模板 compmgmt.msc---计算机管理conf-------启动netmeetingcys--配置您的服务器dcomcnfg.exe--组件服务dcpol.msc--域控制器策略 filesvr.msc--文件…

拦截导弹

拦截导弹 时间限制:3000 ms | 内存限制:65535 KB难度:3描述某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发…

C标准库

嘤嘤嘤,C标准库,我来了转载于:https://www.cnblogs.com/Fsiswo/p/8116331.html

你确定你会使用git commit?

我记得刚工作的时候,我第一次写git commit的内容,直接上来就是一个git commit -m "加上自己的注释",就这样提交了。后来,我去了中兴,又去了恒大,接触到很多人做项目,发现做项目的时候…

adb devices unauthorized解决方法

有时候使用adb连接手机时,即使打开了usb调试,手机添加了信任,仍然出现unauthorized的提示 解决办法如下: 先上两张stack overflow上面的图片: 很多人可能看不懂。翻一下大概如下: 也就是在 C:\Users\xxx\.a…

可怕,别人把我MCU固件给反汇编了!

相关文章很多人都不清楚HEX文件格式CPU怎么认识代码的?一个超强的逆向分析软件本文主要跟大家分享一个简单逆向stm32固件程序的实例,为了让大家在一款成熟的产品中去考虑加密这一块的技术,不然分分钟被别人copy!2、情景再现咬金&a…

GridView相关问题汇总

<asp:gridview id"gvData" runat"server" autogeneratecolumns"False"> <columns> <asp:templatefield headertext"所属系统"> <itemtemplate> <asp:label id…

带参数的宏定义

不是进行简单的字符串替换&#xff0c;还要进行参数替换。其定义的一般形式为#define 宏名(参数表) 字符串字符串中包含在括弧中所指定的参数。如&#xff1a;#define S(a,b) a*bareaS(3,2);定义矩形面积S&#xff0c;a 和 b 是边长。在程序中用了S(3,2)&#xff0c;把3、2分别…

数据结构——堆栈

数据结构——堆栈 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、特性&#xff1a;先进后出&#xff08;FILO&#xff09; 2、应用&#xff1a; 子程序的调用 处理递归调用 表达式转换与求值 二叉树的遍历 图形的深度优化优先 3、用数组仿真堆栈 &a…

处理String类常用的方法1

public class Dome{public static void main(String[] args) {char[] chnew char[] {你,你,你};String strnew String(ch);System.out.println(str);char[] qwestr.toCharArray();} }此为数组转化为字符串&#xff0c;然后字符串又转回数组的方法。 public static void main(St…

电脑控制android手机神器,scrcpy

我同事有一个华为手机&#xff0c;手机里内置了NFC&#xff0c;然后把手机靠近下电脑&#xff0c;当然的&#xff0c;一定需要是华为笔记本&#xff0c;然后电脑就弹出一个界面&#xff0c;控制手机的界面&#xff0c;这个功能非常好&#xff0c;加上NFC的功能&#xff0c;体验…