Android SDCard Mount 流程分析(一)

本文详细介绍了Android系统中SDCard的Mount流程,包括系统启动时如何监听Mount服务、默认设备节点的位置、vold.fstab配置文件的作用及解析过程,并深入探讨了vold启动页面main的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误。今天重新梳理了头绪,针对mount的流程再重新分析一次。

 本篇大纲

  • android 系统如何开机启动监听mount服务
  • 默认设备节点在Android 系统的哪个目录
  • vold.fstab 配置文件的分析 
  • vold 里面启动页面main做了些什么

android 系统如何开机启动监听mount服务

android sdcard 热插拔监测和执行操作是由一个启动文件vold  所统领的,系统开机会读取初始化配置文件init.rc,该文件位于比如我的板子是:device/ti/omap3evm/init.rc,具体根据自己平台查找。里面有一个是默认启动vold 服务的代码,如下:

service vold /system/bin/vold
    socket vold stream 0660 root mount
    ioprio be 2 

 如果要对该文件做出修改之类,要重新编一下boot.img 镜像文件,烧录进android 系统,之后可以在android的文件系统根目录找到init.rc文件。上述代码为启动vold 启动文件,也可以在init.rc 增加多一些我们想要的文件目录,比如增加一个可以存放多分区挂载的目录等,这个是后话。

 

 默认设备节点在Android 系统的哪个目录

 usbdisk 或者 sdcard 热插拔的时候,kernel 会发出命令执行mount或者unmount 操作,但这都是驱动级的。而mount 目录会在android 的文件系统目录下:/dev/block/vold 这个目录由vold 生成,用来存放所有的usbdisk 或者 sdcard 的设备节点。代码位于main里面最优先执行:

 

mkdir("/dev/block/vold", 0755); 

 

 可以根据这个目录找到如下节点:

sh-4.1# ls /dev/block/vold/
179:0  179:1  8:0    8:1    8:2    8:3    8:4 

节点的小介绍:

0代表当前的整个设备,1代码当前设备的分区名称代号。

所以你会发现,sdcard只有一个分区它却生成了两个如:179:0 179:1

而usbdisk 有四个分区,它会生成五个设备节点: 8:0    8:1    8:2    8:3    8:4  就是这个原因。

 

 

 vold.fstab 配置文件的分析

vold 里面会通过指定文件来读取预先配置好的sdcard或者多分区配置文件,该文件位于

/system/core/rootdir/etc/vold.fstab

如以下的配置文件为:

dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1

 

 dev_mount 代表挂载格式

 sdcard 代表挂载的标签

/mnt/sdcard 代表挂载点

 auto 为自定义选项可以为任何,但必须在main 里面自己判断比如这里的意思为自动挂载

后面两个目录为设备路径,第一个如果被占用会选择第二个

 

配置文件可以根据自己的需要编写,并不是固定的,但最好遵循google vold 启动文件代码的格式编写,要不然会给我们修改代码或者增加多分区功能带来不小的麻烦,如以下我自己编写的多分区挂载支持vold.fstab 配置文件:

 

 dev_mount sdcard external /mnt/sdcard auto /devices/platform/mmci-omap-hs.0/mmc_host/mmc0 /devices/platform/mmci-omap-hs.0/mmc_host/mmc1
dev_mount usb1 external /mnt/usbdisk/usb1-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.1/
dev_mount usb2 external /mnt/usbdisk/usb2-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.2/
dev_mount usb3 external /mnt/usbdisk/usb3-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/

 该文件修改后经系统编译会在android 系统目录里/system/etc/vold.fstab找到。

 /devices/platform/ehci-omap.0/usb1/1-2/1-2.1/  代表要挂载的USB口。

vold.fstab 只是一个单纯的配置文件,具体的读取和取数据还 是要靠main里面的process_config函数。看代码,里面正有一段用来读取配置文件:

  if (!(fp = fopen("/etc/vold.fstab", "r"))) {
        return -1;
    }

在这个函数里面会根据读取到的数据存放起来,然后满足条件时执行操作。比如代码里面的:

 

if (!strcmp(type, "dev_mount")) {
            DirectVolume *dv = NULL;
            char *part;

            if (!(part = strtok_r(NULL, delim, &save_ptr))) {
                SLOGE("Error parsing partition");
                goto out_syntax;
            }
            if (strcmp(part, "auto") && atoi(part) == 0) {
                SLOGE("Partition must either be 'auto' or 1 based index instead of '%s'", part);
                goto out_syntax;
            }

            if (!strcmp(part, "auto")) {
                dv = new DirectVolume(vm, label, mount_point, -1);
            } else {
                dv = new DirectVolume(vm, label, mount_point, atoi(part));
            }

            while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {
                if (*sysfs_path != '/') {
                    /* If the first character is not a '/', it must be flags */
                    break;
                }
                if (dv->addPath(sysfs_path)) {
                    SLOGE("Failed to add devpath %s to volume %s", sysfs_path,
                         label);
                    goto out_fail;
                }
            }

            /* If sysfs_path is non-null at this point, then it contains
             * the optional flags for this volume
             
*/
            if (sysfs_path)
                flags = parse_mount_flags(sysfs_path);
            else
                flags = 0;
            dv->setFlags(flags);

            vm->addVolume(dv);
        }

 

DirectVolume后面会讲到,执行mount 和unmount 都是它在做。

另外,有时后读取配置文件会有问题,这是因为它读取是通过指标下标递增的方式在读,如果有问题可以跟踪打印一下配置文件,看哪里需要修改。

 

 vold 里面启动页面main做了些什么

main 主要是初始化socket 连接监听数据变化,在系统起来时第一时间启动,并且通过读取配置文件来识别usb口或者sdcard 的设备地址,来mount 或者unmount 。其它执行mount 、 unmount  或者删除节点等操作都是由上层或者framework 发送命令给main让其通知volumeManage 执行相应的操作。

<think>嗯,用户这次想深入了解Android开机自动挂载存储设备的机制。结合之前讨论过的dm-verity,看来用户对Android存储系统的底层原理很感兴趣。用户引用了三段资料,分别是storage_list.xml配置文件、MountService初始化流程和内核启动过程,说明用户已经做过些调研。 从技术实现来看,这个问题需要分三个层面回答:首先是内核启动时的虚拟文件系统加载,这是挂载的基础;然后是init进程解析fstab文件建立基础挂载点;最后是MountService这个Java层服务如何完成可移动存储的挂载。用户特别提到"自动挂载",所以重点要解释广播触发机制和工作线程的设计。 用户引用的storage_list.xml很有意思,这个配置文件决定了哪些存储设备需要被管理。考虑到用户可能是ROM开发者或系统工程师,应该强调如何自定义这个文件。另外用户上次问过dm-verity,这次在解释/system挂载时可以再提下验证流程的衔接。 需要特别注意内核启动阶段:initramfs解压到内存形成临时根文件系统,这个步骤对后续挂载至关重要。用户引用的资料3提到vmlinuz和initramfs的加载顺序,这个细节要讲清楚,最好用启动流程图辅助说明。 对于MountService的实现难点,要解释为什么需要单独的工作线程——毕竟SystemServer主线程要处理所有系统服务,存储操作又比较耗时。用户引用的资料2提到USB状态广播,这部分可以展开说明热插拔的处理逻辑。 最后生成的问题要兼顾理论和实践:既要有配置修改的具体方法,也要有架构设计层面的思考,这样能覆盖不同层次用户的需求。那个关于fstab和storage_list.xml差异的问题特别实用,很多开发者会混淆这两个配置文件。</think>### Android 开机自动挂载存储设备的机制及实现方法 #### 、核心机制概述 Android 开机挂载通过 **内核层→Native层→Java层** 三级协作实现: 1. **内核层**:加载虚拟文件系统 (`initramfs`) 2. **Native层**:`init` 进程解析 `/fstab` 挂载基础分区 3. **Java层**:`MountService` 管理可移动存储设备[^2] #### 二、详细实现流程 ##### 阶段1:内核初始化 (Kernel Level) 1. **加载临时根文件系统** 内核解压 `initramfs` 到内存,建立临时根目录: ```bash # 内核启动日志示例 Unpacking initramfs... done Mounting tmpfs at /root ``` 2. **执行初始化程序** 启动第个用户空间进程 `/init`(PID=1) ##### 阶段2:Native 层挂载 (Init Process) 1. **解析设备映射表** `init` 读取 `/fstab.<device>` (如 `fstab.exynos`),格式示例: ```ini #<src> <mnt_point> <type> <flags> <options> /dev/block/sda1 /system ext4 ro,barrier=1 wait /dev/block/sda2 /data f2fs nosuid encryptable ``` - `wait`:阻塞启动直到挂载完成 - `encryptable`:启用 FBE 加密 2. **创建基础挂载点** 执行 `mount_all /fstab.<device>` 挂载: ```c // system/core/init/builtins.cpp Result<void> MountAll(const std::string& fstab_file) { Fstab fstab; if (!ReadFstabFromFile(fstab_file, &fstab)) return ErrnoError(); return MountAll(fstab); // 实际挂载操作 } ``` ##### 阶段3:Java 层挂载 (MountService) 1. **初始化存储配置** 读取 `storage_list.xml` 定义的可移动存储: ```xml <!-- frameworks/base/core/res/res/xml/storage_list.xml --> <storage android:mountPoint="/mnt/sdcard" android:storageDescription="@string/storage_sd_card" android:removable="true" <!-- 关键:标记为可移动 --> android:primary="false"/> ``` [^1] 2. **启动挂载服务** `SystemServer` 创建 `MountService` 并注册关键监听器: ```java // frameworks/base/services/java/com/android/server/SystemServer.java mountService = new MountService(context); ServiceManager.addService("mount", mountService); // 注册广播接收器 mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { mountService.mountAllVolumes(); // 开机完成时触发挂载 } } }; ``` 3. **异步挂载机制** - 专用工作线程处理挂载任务,避免阻塞主线程 - 通过 `HandlerThread` 实现消息队列: ```java // frameworks/base/services/core/java/com/android/server/MountService.java private final HandlerThread mHandlerThread = new HandlerThread("MountService"); private final Handler mHandler; public void mountAllVolumes() { mHandler.post(() -> { for (StorageVolume volume : mVolumes) { if (volume.isRemovable()) { mountVolume(volume); // 实际挂载调用 } } }); } ``` [^2] #### 三、关键技术点解析 1. **热插拔支持** 通过监听 `USB_STATE_CHANGED` 广播实现动态挂载: ```java // 监听USB状态变化 filter.addAction(UsbManager.ACTION_USB_STATE_CHANGED); context.registerReceiver(mBroadcastReceiver, filter); ``` 2. **权限控制模型** 挂载时应用 SELinux 策略: ```bash # 设备上下文示例 /dev/block/mmcblk0 u:object_r:sdcard_device:s0 /mnt/media_rw u:object_r:media_rw_file:s0 ``` 3. **异常处理机制** - 挂载失败时重试机制(最多3次) - 错误日志记录到 `dmesg` 和 `logcat` #### 四、挂载流程时序图 ```mermaid sequenceDiagram Kernel->>initramfs: 解压临时根文件系统 initramfs->>init: 启动第个进程 init->>fstab: 解析设备映射表 init->>/system: 挂载系统分区 SystemServer->>MountService: 创建服务实例 MountService->>storage_list.xml: 读取存储配置 Note over MountService: 注册BOOT_COMPLETED监听 BroadcastReceiver->>MountService: 发送挂载指令 MountService->>HandlerThread: 提交异步任务 HandlerThread->>vold: 通过Binder调用挂载命令 vold->>Kernel: 执行mount()系统调用 ``` #### 五、自定义配置指南 1. **添加新存储设备** 修改 `storage_list.xml` 增加条目: ```xml <storage android:mountPoint="/mnt/custom" android:storageDescription="@string/storage_custom" android:removable="true"/> ``` 2. **调整挂载参数** 在 `fstab` 中修改选项: ```ini /dev/block/mmcblk1p1 /mnt/sdcard exfat defaults,noatime,uid=1000,gid=1015 0 0 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值