Android Fuse文件系统-7:passthrough方案

1,671 阅读3分钟

一、命名空间mount+sdcardfs

核心原理概述:
  • 在前边的源码分析中可以看到,MediaProvider 是直接访问底层文件系统 /data/media 目录
  • 其能直接访问 /data/media 目录的关键原因是:
    • Zygote 进程会因传入 mount_mode 参数,做不同的 bind mount 操作
    • 当为 MOUNT_EXTERNAL_PASS_THROUGH 时,把 /storage 目录指向 /mnt/pass_through/userid
    • 而 /mnt/pass_through/0/emulated 在开机启动过程的mount服务启动时,直接指向了底层目录 /data/media
    • 所以 mount_mode 这个参数是个核心突破点:让其他App进程创建时,也传入此 MOUNT_EXTERNAL_PASS_THROUGH 值,即可直接访问底层目录 /data/media
  • mount_mode 哪里来的?
    • 在进程创建过程中,会调用 StorageManagerService 中的 getMountModeInternal(int uid, String packageName) 获取
  • 修改此处代码可以解决 直接访问底层目录 /data/media 问题
  • 能直接访问底层目录 /data/media 了,是不是就可以了?
  • 并不是
  • App进程直接访问底层文件,会导致“权限问题”。
    • sdcard目录下的文件是共享的。比如图片,需要任意进程都可以访问。
    • 已知,android App应用进程会分属不同的“用户”,这个是linux“用户”概念,是uid,android自己的“用户”概念使用的userid
    • 要想保证不同App都能正确访问sdcard目录文件,需要所有文件都是相同的权限,即 adb shell ls -l 看到的权限是一样的
    • (。。。。省略N段关于文件权限的内容:用户,群组,9位权限位,10位,12位,更多。umask,进程与文件权限关系)
  • 解决方案:sdcardfs
  • android8~10版本用的 sdcardfs 文件系统,三星公司开发。
  • sdcardfs 也是个中间层。App访问sdcard目录下的文件时,先通过 sdcardfs。
    • 写时,sdcardfs 会把所有文件的权限变成一样的。
    • 读时,sdcardfs 又会以带有同样进程权限的
  • 所以,需要同时使用 sdcardfs
  • msm-5.4 内核,删除了 sdcardfs 。因此要使用的话需要移植 sdcardfs,sdcardfs内容很少,相比fuse的代码逻辑要简单很多,移植起来不算麻烦

二、内核passthrough方案概述:

核心原理简述:
  • 仅仅针对读写优化。
  • create 和 read write 操作是分开的,权限的是在文件 create 时一起确定的,而且文件的 read write 操作最耗时,
  • 所以,create open 由用户空间的 FuseDaemon 处理,read write 操作不再转发给用户层的 FuseDaemon ,直接在内核空间处理掉。
  • 缺点:在读写大文件时,性能和直接访问底层文件系统相似。但读写大量小文件时,速度很慢,因为open等操作耗费大量时间。
sequenceDiagram
participant App
participant FuseDaemon
participant libfuse_fuse_lowlevel
participant kernel_dev.c
participant kernel_file.c

App ->> FuseDaemon: pf_access
	FuseDaemon ->> FuseDaemon : do_passthrough_enable
		FuseDaemon ->> libfuse_fuse_lowlevel: fuse_passthrough_enable
			libfuse_fuse_lowlevel ->> kernel_dev.c: fuse_dev_ioctl
				kernel_dev.c ->> kernel_passthrough.c: fuse_passthrough_open保存cred数据,这里边保存了进程的权限
				
App ->> kernel_file.c : 调用write方法时,最终调用fuse_file_write_iter 
	kernel_file.c ->> kernel_passthrough.c: fuse_passthrough_write_iter 取出保存的cred数据,覆盖当前进程的cred

三、针对文件管理器的优化

内核passthrough方案 依旧有缺点,单独大文件的复制速度和原有的sdcardfs差不多,

但是,大量小文件的复制速度依旧下降明显。

对于此,可以针对性的对系统的文件管理器做优化:

  • 1、针对文件系统app,可以从framework代码中固定其uid

  • 2、在内核中判断此uid,如果是文件管理器的uid直接以 MediaProvider 的uid直接去读写文件

    • 就是说连 open 也不经过fuse用户空间。

或者直接配置sharedUserId,同 MediaProvider 共用同一个 sharedUserId