zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

linux fuse文件系统在 android fuse sdcard的 运用

AndroidLinux 文件系统 运用
2023-09-14 09:16:12 时间

一、android GB 及JB、KK版本内置sdcard效果对比图


从上面效果对比图,我们可以发现android fuse sdcard 有如下两个优点:

1、使用fuse后 /data 和  /sdcard0 是共离一块分区,这块分区的空间/data和/sdcard0 动态享用, 用户使用灵活。

2、去掉了fat32文件系统,这样也免去了一个license的风险。


二、fuse的标准工作流程图

from:  http://en.wikipedia.org/wiki/Filesystem_in_Userspace

Filesystem in Userspace
FUSE structure.svg

这里重点说明libfuse的作用:libfuse为开发者提供了接口fuse_operations开发者只需要实现这组接口,然后调用fuse初始化接口:fuse_mount()、fuse_new()、fuse_loop()即可实现一个用户空间文件系统。这样为开发多种fuse文件系统带来很多方便。


三、android fuse sdcard架构图及source code

1、source code:

\system\core\sdcard\
\frameworks\base\cmds\installd\
\kernel\fs\fuse\

2、棱流程架构图:




android fuse sdcard  流程和标准fuse流程图最大不同点在于:

android没有直接移植标准的libfuse,而是重写了相关代码,将libfuse的功能集成到sdcard dameon。

这样做的我能想到的好处,可能就是函数调用的层次少了一些,可能效率会好点。

一直想不明白android为什么不移植libfuse。


3、fuse sdcard 的mount状态,如下图:





四、使用fuse sdcard 带来的一些问题及解决方法

1、/data和/sdcard 动态占用空间,如果用户通过/sdcard将整个分区填满,则会导致系统无法启动。

  解决办法,就是设置一个/sdcard/可用的上限,不至于导致系统崩溃至无法启用。

 具体可以参考mtk修改的代码:\kernel\fs\fuse\inode.c (LIMIT_SDCARD_SIZE包宏处)

static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
{
stbuf->f_type    = FUSE_SUPER_MAGIC;
stbuf->f_bsize   = attr->bsize;
stbuf->f_frsize  = attr->frsize;
stbuf->f_blocks  = attr->blocks;
stbuf->f_bfree   = attr->bfree;
stbuf->f_bavail  = attr->bavail;
stbuf->f_files   = attr->files;
stbuf->f_ffree   = attr->ffree;
stbuf->f_namelen = attr->namelen;
#ifdef LIMIT_SDCARD_SIZE
stbuf->f_blocks  -= (u32)data_free_size_th/attr->bsize;

if(stbuf->f_bfree < ((u32)data_free_size_th/attr->bsize)){
stbuf->f_bfree = 0;
}else{
stbuf->f_bfree-= (u32)data_free_size_th/attr->bsize;
}
if(stbuf->f_bavail < ((u32)data_free_size_th/attr->bsize)){
stbuf->f_bavail = 0;
}else{
stbuf->f_bavail-= (u32)data_free_size_th/attr->bsize;
}
#endif
/* fsid is left zero */
}


2、清除用户数据或者recovey、ota等涉及到要format /data 目录时,/storage/sdcard0 的数据也会被清空掉。

 解决办法: 

       •如果需要清除data时(format /data),不直接格式化,而采用删除方式(白名单/data/media/

3、不同size emmc 的兼容:同一款手机,可能需要兼容多个size emmc,比如16G版本,32G版本。

  
  build阶段,userdata.img指定分区的大小是不变的,但不同emmc size,“/data”分区的大小是希望不同的。
  解决方法:利用ext4 resize功能,第一次开机自动调整“/data”分区的大小。
   source code :\external\e2fsprogs\resize
   http://www.ibm.com/developerworks/cn/linux/l-cn-ext4resize/

   后面再专门写一篇文章说明resize功能的实际使用。

4、厂家的预置资源文件,如何导入到内置sdcard?

     手机出货前,厂家通常会预置一些资源文件,比如,导航地图,广告视频等。 

     解决方法:将预置资源编译到/data/media/目录下,系统第一次启动时installd进程会自动将/data/media/目录的东西移到/data/media/0 目录,即sdcard根目录可见该预置资源。

   见如下代码:frameworks\base\cmds\installd\installd.c

 int initialize_directories() {
    int res = -1;


    // Read current filesystem layout version to handle upgrade paths
    char version_path[PATH_MAX];
    snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);


    int oldVersion;
    if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
        oldVersion = 0;
    }
    int version = oldVersion;


        // /data/media.tmp
            char media_tmp_dir[PATH_MAX];
            snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);


            // Only copy when upgrade not already in progress
            if (access(media_tmp_dir, F_OK) == -1) {
                if (rename(android_media_dir.path, media_tmp_dir) == -1) {
                    ALOGE("Failed to move legacy media path: %s", strerror(errno));
                    goto fail;
                }
            }


            // Create /data/media again
            if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
                goto fail;
            }


            // /data/media/0
            char owner_media_dir[PATH_MAX];
            snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);


            // Move any owner data into place
            if (access(media_tmp_dir, F_OK) == 0) {
                if (rename(media_tmp_dir, owner_media_dir) == -1) {
                    ALOGE("Failed to move owner media path: %s", strerror(errno));
                    goto fail;
                }
            }


      version = 2;

         // Persist layout version if changed
    if (version != oldVersion) {
        if (fs_write_atomic_int(version_path, version) == -1) {
            ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
            goto fail;
        }
    }


    // Success!
    res = 0;

   }


 通过上面的方法,确实可以将预置资源导入到sdcard,但在实际大量生产中,发现一个新问题:

预置资源可能会被移到/sdcard/0/ 目录(即/data/media/0/0),多了一级0目录。


为什么会发生这种问题呢? 经过长时间分析,应该是因为上面的代码稳定性极度依赖于函数fs_write_atomic_int()的原子操作性。

但实际上,该函数根本达不到原子操作效果。

如果解决该问题呢? 1、取消多用户   2、fs_write_atomic_int()函数后面添加sync()。


5、fuse sdcard对开机速度的影响

    由于data和sdcard共享分区后, /data分区变大了很多,特别是32G、64G等大容量emmc 。
    这样系统启动时,调用如下命令,fs check花的时间就会长很多。
    •exec /sbin/e2fsck -pfD /emmc@usrdata  

  解决方法: 正常开机,不进行完整的check流程。 仅异常开机才进行完整fs check 。类似于PC 。

6、fuse sdcard 相对fat32 sdcard性能更差

  
   fuse sdcard 相比fat32 sdcard 读写性能会有15%左右的drop,这是fuse的 设计天性, 各位有什么好的解决方法呢?