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

转:http://www.sjsjw.com/kf_jiagou/article/023422ABA014753.asp
1、/data和/sdcard 动态占用空间,如果用户通过/sdcard将整个分区填满,则会导致系统无法启动。

设置内部sdcard存储下限,sdcard存储下限

在Android中,内部存储有一部分区域是必须预留出来供系统运行应用程序的。

但是在Android原生设计中没有考虑这点,内部存储是可以完全填充满的。

这样会导致系统在运行程序,尤其是需要操作数据库的程序时,出现SQLiteFullException的错误。

解决的方法是在sdcard.c文件中加入一个限制,比如限制当存储低于100M时,不再允许第三方应用存储媒体文件。

如adb push 文件到/storage/sdcard0/中,或录音,拍照等保存到/storage/sdcard0/中。

这100M的空间专门预留出来保证系统程序的运行。

sdcard.c文件所在路径为源码中:system/com/sdcard/sdcard.c

修改完后可直接编译sdcard目录,会生成一个sdcard文件,push到系统/system/bin目录下,重启后即生效。

修改代码:

1.在sdcard.c的开始位置定义宏

// begin:SQLiteFullException happened when device memory is empty.

#define LIMIT_USEDATA_SIZE (100 * 1024 * 1024)

// end:SQLiteFullException happened when device memory is empty.

2.在fuse结构体中增加变量free_blksize
struct fuse {
   ......
    __u64 next_generation;

    // begin:SQLiteFullException happened when device memory is empty.
    __u64 free_blksize;

    // end:SQLiteFullException happened when device memory is empty.

   ......

}

3.在init方法中做初始化

static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
        gid_t write_gid, derive_t derive, bool split_perms) {
    ......
    fuse->next_generation = 0;
    // begin:SQLiteFullException happened when device memory is empty.
    struct statfs stat;
    if (statfs(source_path, &stat) < 0) {
        fuse->free_blksize = 0;
    } else {
        fuse->free_blksize = stat.f_bfree * stat.f_bsize;
    }
    // end:SQLiteFullException happened when device memory is empty.

    fuse->derive = derive;

    ......

}

4.在handle_write方法中实现功能

static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
        const struct fuse_in_header* hdr, const struct fuse_write_in* req,
        const void* buffer) {
    ......
    TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token,
            h, h->fd, req->size, req->offset);
    // begin:SQLiteFullException happened when device memory is empty.
    if (!strncmp(fuse->root.name, "/data/media", fuse->root.namelen)) {
        pthread_mutex_lock(&fuse->lock);
        fuse->free_blksize -= req->size;
        pthread_mutex_unlock(&fuse->lock);

        if (fuse->free_blksize <= LIMIT_USEDATA_SIZE) {
            struct statfs stat;
            if (statfs(fuse->root.name, &stat) < 0) {
                fuse->free_blksize = 0;
                return -errno;
            } else {
                pthread_mutex_lock(&fuse->lock);
                fuse->free_blksize = stat.f_bfree * stat.f_bsize;
                pthread_mutex_unlock(&fuse->lock);
            }
            errno = ENOSPC;
            return -errno;
       }
    }
    // end:SQLiteFullException happened when device memory is empty.
    res = pwrite64(h->fd, buffer, req->size, req->offset);

    ......

}



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, "%s 0 ", 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目录。  


为什么会发生这种问题呢? 经过长时间分析,应该是因为上面的代码稳定xìng极度依赖于函数fs_write_atomic_int()的原子操作xìng。  
但实际上,该函数根本达不到原子操作效果。  
如果解决该问题呢? 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 sdcardxìng能更差

    
   fuse sdcard 相比fat32 sdcard 读写xìng能会有15%左右的drop,这是fuse的 设计天xìng;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值