sdcardfs 浅析
内核工匠 2022-09-29


sdcardfs是三星基于wrapfs框架开发的虚拟文件系统,并凭借其出色的IO性能,在Android O上替代FUSE(File system in Userspace),成功上位。不提FUSE单讲sdcardfs的文章算不上一次齐全的解析,所以本文在介绍sdcardfs的同时,也会对比sdcardfs和FUSE的框架与原理、分析为何FUSE被sdcardfs替代。希望通过对比解析清楚,那让我们一步步走进sdcardfs。



sdcardfs的诞生



sdcardfs是基于wrapfs开发的。wrapfs是什么、又是如何在FUSE,EXT的文件系统大家族中拥有一席之地呢?我们借助wrapfs的开发者Erez对文件系统的分类来分析一下:

表1.文件系统的分类


大多数文件系统可以归纳到表1总结的三类中。第一类是底层文件系统,内核空间的优势保障了这类文件系统的优越性能,但驾驭这类文件系统要求开发者对操作系统有深入理解,并不利于开发和调试。第二类用户层文件系统解决了这个痛点,用户层文件系统将晦涩难懂的底层文件系统藏在内核中,提供接口方便开发者调用开发新的文件系统。然而成也用户层,败也用户层,虽然比底层文件系统更容易开发与调试,但在用户态和内核态的来回切换导致性能大大降低。


鱼与熊掌不可兼得的困境推动了第三种文件系统的诞生---可堆叠文件系统。它的性能表现接近底层文件系统,而开发成本接近用户层文件系统。wrapfs作为一种可堆叠文件系统的框架,为sdcardfs提供必需、或可选择的函数来帮助开发者开发。处于内核空间和用户空间的中间层的sdcardfs,并不真正地管理磁盘而是将操作命令和参数由上层传给底层。


sdcardfs首先由三星开发,后由Google接手并应用于Android O,然而在AOSP官方承认sdcardfs之前,sdcardfs已经被众多手机厂商商用,比如华为也有自己开发的一套sdcardfs。



为sdcardfs解除误会



在梳理sdcardfs是如何运作之前,有几点常年围绕着sdcardfs的困惑点要先说明清楚。如果你也是第一次接触sdcardfs,一定会奇怪,sdcardfs是sd卡的文件系统吗?\sdcard分区又和他们是什么关系呢?


我们先来说说sd卡和\sdcard分区,早期的安卓手机因为内部存储空间太小,所以会使用sd卡这种外部存储来增大可用空间。与此同时\sdcard分区也出现了,主要存储一些音乐电影等文件。然而这并不代表\sdcard分区就归sd卡所用。在某些同时拥有内部存储空间和sd卡的设备上,\sdcard实际代表内部存储空间,而\storage\sdcardfs1指外部存储。可以说sd卡的流行确实引出了\sdcard分区,但是这两者之间并没有一对一的所属关系。


另外,sdcardfs文件系统也不是为了管理sd卡而开发的文件系统。而是为了解决其他两类文件系统的痛点而开发的。这就不得不先说说FUSE是怎么来的:当年的文件系统并没有权限管理的能力,当应用A和应用B同时将数据存在外部存储中,并且同时拥有外部存储的读写权限时,也意味着应用A、B有权限读到对方的数据。这是极其不安全的,尤其当应用的敏感信息也存储在外部存储的时候。所以FUSE的一大功能就是赋予权限管理的能力。然而FUSE受自身框架限制,不可避免地拥有IO latency,double caching等痛点得不到解决,以至于一度被sdcardfs所替代。


sdcardfs也拥有case folding, 权限管理等其他功能。而本文作为入门级浅析,主要集中在sdcardfs的框架梳理和对比中,会更详细分析sdcardfs和FUSE框架有何不同,sdcardfs又是如何避免了FUSE的痛点。



框架梳理-始于挂载



和其他文件系统一样,使用sdcardfs也需要先挂载。在init_sdcardfs_fs()中首先为inode,dentry,packagelist申请slab缓存,然后调用resigter_filesystem注册文件系统。和init_sdcardfs_fs()对应自然会有exit_sdcardfs_fs()这个函数来注销sdcardfs,虽然并不常用。



框架梳理-没有对比就没有伤害



为了更好地理解sdcardfs何以替代FUSE,这里先梳理一下FUSE框架:


图1.FUSE框架


作为中间层的角色,FUSE拥有可加载的内核驱动模块(图中蓝色部分)以及提供API的用户空间的库(图中绿色部分)。其中内核驱动模块注册了/dev/fuse块设备来支持和用户态的通信,而用户空间的守护进程则时刻准备从/dev/fuse中读取并处理操作命令,然后将结果通过/dev/fuse返回给内核空间。用户空间提供的接口方便开发者忽略底层文件系统的存在,基于libfuse实现自定义的操作命令的处理方式。


当操作挂载FUSE的路径时,用户的命令会随着图中的橘色剪头所示,经过如下一段旅程:1)当用户产生操作命令时,会通过glibc的系统调用将命令传递到内核层的VFS;2)VFS根据所在分区的文件系统将命令传递给对应文件系统的接口,这里挂载的文件系统当然是FUSE;3)FUSE内核模块收到命令后,会将命令分类放在某个队列中等待处理(包括pending,background,processing,interrupts,forgets五个队列,这里不展开讨论队列策略,感兴趣可参考文献1的3.1.1章节[1])/dev/fuse块设备会通过既定的通信协议将操作请求发送给上层FUSE守护进程来“处理”请求,而如何处理操作请求,取决于开发者在守护进程中的具体实现,这就是用户态文件系统开发者需要开发的部分。4)FUSE的守护进程将操作处理完后,将处理结果返回块设备/dev/fuse;5)FUSE内核模块将处理结果返回给VFS,VFS再将结果原路返回给上层。


分析可发现整个流程中,发生了六次内核态与用户态的切换,这种高负担的操作解释了为何用户层文件系统的性能差。而FUSE确确实实减少了开发成本,如图所示,开发者并不需要了解底层文件系统如何工作,甚至不需要深入理解内核,根据libfuse提供的API接口可以快速开发出简单的定制化的文件系统。


sdcardfs作为位于内核的中间层,和底层文件系统相比也减少了开发成本,并且处理操作命令的流程非常直观清晰,没有FUSE那么弯弯绕绕的流程:


图2.带sdcardfs后的框架


如图所示,sdcardfs仍然处于内核空间。它不同于FUSE,不是内核态和用户态的中间层,而是处于VFS与底层文件系统的中间。在没有sdcardfs的时候,虚拟文件系统层将操作命令直接向下传给指定的文件系统,比如EXT4或F2FS;加上sdcardfs之后,操作命令会经过VFS->sdcardfs->底层文件系统。流程简洁明了无需多言,问题的关键在于,sdcardfs如何将操作命令中的数据快速准确地转发给底层文件系统,并且不会因为sdcardfs自身的存在而影响原来的操作性能。


在底层文件系统中,我们需要inode、dentry、file多个struct来定位某个文件或数据的所在位置。那么多了一层sdcardfs之后,如何保证IO性能呢?sdcardfs文件系统内也构建对应的inode、dentry、file结构体,并和底层文件系统的inode、dentry、file结构体链接起来,如图所示:


图3.sdcardfs与底层文件系统的链接[3]


通过图3的链接,如果知道sdcardfs的file结构体,可以找到底层文件系统的file结构体,继而定位其dentry和inode等信息。



sdcardfs VS FUSE



sdcardfs解决了FUSE的很多问题才成功上位的,比如上文提到的IO latency以及double caching。


IO latency:依据第二节提到的FUSE的框架图,具体分析可得每一个读写命令都将经过六次的用户态和内核态的转换:1)用户空间的应用将系统调用传给FUSE内核驱动;2)FUSE内核驱动通知FUSE用户态守护程序有新的操作命令,用户态守护程序从dev/fuse读取命令;3)守护程序分析命令并下传给底层文件系统;4)底层文件系统执行操作,并将数据返回给FUSE守护程序;5)守护程序将结果通过dev/fuse传给FUSE内核驱动;6)FUSE内核驱动将结果返回给上层应用结束。而在sdcardfs的框架下,内核中仍然只是open, read, close三步操作。


这种架构的劣势尤其体现在多个小文件的操作上。每一个小文件都将引起六次用户态和内核态的切换,这种框架下的用户态文件系统操作速度远远低于底层文件系统:在复制10000个50kb的小文件的实验中,FUSE比没有FUSE慢了40秒[2],如图4所示,在没有FUSE中间层的EXT4文件系统下,复制10000个50kb小文件需要17.27秒,而FUSE框架下需要1分03秒03毫秒。


而sdcardfs从架构上避免了不用空间的频繁切换,将切换次数从6次减少到必需的2次切换,即操作命令下发和结果返回,使其性能接近底层文件系统。


图4.复制大量小文件性能对比


Double caching:在FUSE框架中的每一次读写调用,数据都会从内核内存复制到用户层缓存,原本的page cache因为FUSE的存在,又大了一倍。而sdcardfs只作为操作和参数的传递者而非处理者,所以从架构中避免了重复缓存的问题。



总结



本文基于与FUSE的对比,浅析了sdcardfs的框架和优势。正因为sdcardfs解决了FUSE IO操作延迟和重复缓存的主要痛点,使它在底层文件系统和用户层文件系统的夹缝中生存了下来。sdcardfs已不是当下流行的概念,然而sdcardfs还有很多可提升的空间。


本文篇幅有限,还有很多内容没有覆盖到,比如sdcardfs的权限管理,case folding,storage tracking等功能的介绍。希望这篇会有后续,或者其他同学也针对sdcardfs的功能来篇分析。



参考资料


[1]用户空间文件系统FUSE:架构和实现细节


[2]Diving into SDCardFS:How Google’s FUSE Replacement Will Reduce I\O Overhead


[3]A Stackable File System Interface For Linux, Erez Zadok and Ion Badulescu

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • 电源
  • DC
  • AC
  • 稳压
  • 直流稳压电源的组成

    直流稳压电源的组成直流稳压电源主要由四部分组成:电源变压器、整流电路、滤波电路和稳压电路。1.电源变压器电源变压器是一种软磁电磁元件,功能是功率传送、电压变换和

    昨天
  • 图解小型电源变压器组成

    小型电源变压器主要由铁心、绕组、固定支架、绝缘骨架及绝缘物等组成,如图4—3所示。(1)铁心。常见小型电源变压器的铁心有E形、口形、C形(分cD和ED形)和F形

    昨天
  • 揭秘新老款MacBook Pro的USB Type-C有何不同

      尽管参与USB-IF的多家巨头,像是苹果与Intel都以具体移动告诉市场,USBtype-c就是未来,不过在未来的美好来临前,使用者却仍需面对连接埠的阵痛期

    11-24
  • 利用无线探头测量感应电源的电压频率转换器

    为执行长期监视任务的便携式遥测系统供电,向人们提出了有趣的设计挑战。电池不适合于某些关键性应用,且在这些环境中,设计人员一般用无线感应链路来传输功率与数据。感应链路由一个驱动固定初级线圈的射频发射器与一个为便携式装置提供电源的松耦合次级线圈组成。对设计工程师来说,测量发射功率相当重要,因为它会限制设计人员可包含至便携式装置中的电路数量。但不幸的是,传统测试设备不适合执行该任务,因为标准电压探头会拾...

    11-24
  • 全面讲解pwm波形发生器

    波形发生器在生活中有诸多应用,不过对于波形发生器,大家并非均有所了解。此外,波形发生器种类较多,无法在短时间内全部掌握。本文中,将为大家讲解pwm波形发生器,并

    11-24
  • 线交互式UPS逆变器特性

    一、功能部件输入开关市电正常时,开关导通;市电停电时,开关自动断开,防止逆变器向电网反向馈电。自动稳压有市电供电时,可粗略稳压并吸收部分电网干扰。其稳压方式与后

    11-23
  • LTC3643 用作针对 3.3V 电压轨的备份电源解决方案

    在嵌入式系统需要可靠供电的电信、工业和汽车应用中,数据丢失是一个关切的问题。供电的突然中断会在硬盘和闪存器执行读写操作时损坏数据。我们常常使用电池、电容器和超级

    11-23
  • 如何测量开关电源稳定性

    随着电子,自控,航天,通讯,医疗器械等技术不断向深度和广度的发展,势必要求为期供电的电源要有更高的稳定性,即不仅要有好的线性调节率、负载调节率还要有快速的动态负

    11-23
  • 如何对一个简单的峰值电流限制进行改进

    故障保护是所有电源控制器都有的一个重要功能。几乎所有应用都要求使用过载保护。对于峰值电流模式控制器而言,可以通过限制最大峰值电流来轻松实现这个功能。在非连续反向

    11-23
  • NTC PTC热敏电阻在电源电路中的作用

    本文以问答的形式介绍了NTC PTC热敏电阻在电源电路中的作用。问题1: NTC电阻串联在交流电路中主要是起什么作用!它是怎样工作!请大侠指点!谢谢!问题2:

    11-23
下载排行榜
更多
广告