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
  • 稳压
下载排行榜
更多
评测报告
更多
广告