第七板块:Android 存储体系与文件系统 | 第二十一篇:Vold 与 FUSE 存储架构

24 阅读6分钟

第七板块:Android 存储体系与文件系统 | 第二十一篇:Vold 与 FUSE 存储架构

所属板块:第七板块 — Android 存储体系与文件系统

前置知识:第二十篇中的应用签名、PMS 安全校验、Linux 文件系统(ext4/F2FS)、SELinux 安全上下文

本篇定位:这是 Android 设备数据持久化的基石。如果说内存是暂存的思绪,那么存储就是永久的记忆。本篇将彻底拆解 Vold(Volume Daemon) 的存储管理架构、FUSE(Filesystem in Userspace) 的虚拟挂载机制、多用户存储隔离(Emulated Storage)SD 卡与 OTG 的动态挂载StorageManagerService 的配额管理。我们将深入 Kernel VFSNative DaemonFramework Service,揭示 Android 如何在 Linux 的文件系统之上,构建一套安全、隔离、多用户的存储沙箱。


1. 核心结论先行(Thesis Statement)

Android 的存储体系是一个基于 Linux VFS 的虚拟化层

  • Vold 的本质存储设备的外交官。它是一个 Native 守护进程,负责监听内核的 uevent(如 USB 插入、SD 卡挂载),并通过 Netlink Socket 与 Kernel 通信,管理块设备(Block Device)的生命周期。
  • FUSE 的本质用户空间的伪装者。它允许在用户空间实现一个文件系统,并将其挂载到内核 VFS 中。Android 使用 FUSE 来实现 /storage/emulated/多用户隔离权限控制,而无需修改内核。
  • 存储沙箱的本质路径重定向 + GID 访问控制。应用看到的 /sdcard/ 实际上是 FUSE 挂载点,Vold 通过 GID(Group ID) 控制应用是否能访问外部存储。
  • Mount 的本质将物理设备映射到目录树。这是一个从 Block Device (/dev/block/...) -> Filesystem (ext4/F2FS) -> Mount Point (/data, /storage) 的逐级映射过程。

2. 存储架构全景图

2.1 从物理介质到应用沙箱

graph TB
    subgraph Physical ["物理介质"]
        EMMC["eMMC / UFS 闪存"]
        SD["SD 卡 / TF 卡"]
        OTG["USB OTG 设备"]
    end

    subgraph Kernel ["Linux 内核"]
        Block["块设备层 (Block Layer)"]
        FS["文件系统 (ext4 / F2FS)"]
        VFS["虚拟文件系统 (VFS)"]
    end

    subgraph Userspace ["用户空间"]
        Vold["Vold (Volume Daemon)"]
        FUSE["FUSE Daemon (sdcardd)"]
        PMS["PackageManagerService"]
        App["应用进程"]
    end

    subgraph MountPoints ["挂载点"]
        Data["/data (内部存储)"]
        Emulated["/storage/emulated (虚拟存储)"]
        Raw["/mnt/media_rw (原始存储)"]
    end

    EMMC --> Block
    SD --> Block
    OTG --> Block

    Block --> FS
    FS --> VFS

    VFS -->|"挂载"| Data
    VFS -->|"FUSE 挂载"| Emulated
    VFS -->|"挂载"| Raw

    Vold -->|"管理"| Block
    Vold -->|"启动"| FUSE
    PMS -->|"分配配额"| Vold
    App -->|"访问文件"| Emulated

2.2 核心组件职责表

组件层级职责学术定义
VoldNative设备管理监听 uevent,格式化、挂载、卸载存储设备。
FUSEKernel/Native虚拟文件系统在用户空间实现文件系统逻辑,提供权限过滤。
StorageManagerServiceFramework存储服务管理存储卷的生命周期,提供 API 给应用。
Emulated Storage逻辑虚拟存储基于 FUSE 的多用户隔离存储,替代真实的 SD 卡路径。

3. Vold 的存储管理

3.1 Vold 的启动与监听

Vold 在系统启动时由 init 进程启动,通过 Netlink Socket 监听内核事件。

学术定义

  • Netlink Socket:一种特殊的 Socket,用于内核与用户空间进程通信。Vold 监听 NETLINK_KOBJECT_UEVENT
  • Uevent:内核发出的事件,如 ACTION=add (设备插入)、ACTION=remove (设备拔出)。

3.2 存储卷(Volume)的生命周期

Vold 将存储设备抽象为 Volume

stateDiagram-v2
    [*] --> Unmounted: 设备插入
    Unmounted --> Checking: 开始检查
    Checking --> Mounted: 检查通过,挂载
    Mounted --> Unmounting: 用户请求卸载
    Unmounting --> Unmounted: 卸载完成
    Mounted --> Ejecting: 物理拔出
    Ejecting --> [*]: 消失

3.3 关键源码解析

// system/vold/VolumeManager.cpp
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
    std::string devPath = evt->findParam("DEVPATH");
    std::string action = evt->getAction();

    if (action == "add") {
        // 发现新设备
        auto vol = createVolume(devPath);
        vol->create();
    } else if (action == "remove") {
        // 设备移除
        auto vol = findVolume(devPath);
        vol->destroy();
    }
}

4. FUSE 与多用户存储隔离

4.1 为什么需要 FUSE?

Android 是多用户系统(主用户、访客、工作资料)。如果直接使用 /sdcard,所有用户都能看到彼此的文件。FUSE 解决了这个问题。

学术定义

  • 多用户隔离:FUSE 可以根据调用者的 UID,动态映射到不同的物理目录。
  • 权限控制:FUSE 可以在用户空间检查应用是否有 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限。

4.2 Emulated Storage 的映射

Android 使用 FUSE 创建虚拟存储:

路径实际位置学术定义
/storage/emulated/0/data/media/0主用户的虚拟存储。
/storage/emulated/10/data/media/10工作资料用户的虚拟存储。
/mnt/user/0/emulated/0/storage/emulated/0绑定挂载,用于兼容旧应用。

4.3 FUSE Daemon (sdcardd)

sdcards 是 FUSE 的用户空间守护进程。

工作流程

  1. 应用调用 open("/storage/emulated/0/file.txt")
  2. Kernel VFS 将请求转发给 FUSE 内核模块。
  3. FUSE 内核模块通过 /dev/fuse 通知 sdcardd
  4. sdcardd 检查调用者的 UID 和权限。
  5. 如果允许,访问实际的物理文件 /data/media/0/file.txt
  6. 将结果返回给 FUSE 内核模块,再返回给应用。
sequenceDiagram
    participant App as 应用 (UID 10001)
    participant VFS as Kernel VFS
    participant FUSE_K as FUSE 内核
    participant SDCARD as sdcardd (FUSE Daemon)
    participant Data as /data/media/0

    App->>VFS: open("/storage/emulated/0/file.txt")
    VFS->>FUSE_K: 转发请求
    FUSE_K->>SDCARD: 读取请求
    SDCARD->>SDCARD: 检查 UID 10001 是否有权限
    SDCARD->>Data: 访问 /data/media/0/file.txt
    Data-->>SDCARD: 文件内容
    SDCARD-->>FUSE_K: 返回内容
    FUSE_K-->>VFS: 返回内容
    VFS-->>App: 文件句柄

5. 存储权限与 GID 映射

5.1 外部存储权限

应用访问外部存储需要声明权限。

权限GID学术定义
READ_EXTERNAL_STORAGEAID_SDCARD_R (1028)允许读取 /storage/emulated
WRITE_EXTERNAL_STORAGEAID_SDCARD_RW (1015)允许写入 /storage/emulated
MANAGE_EXTERNAL_STORAGEAID_SDCARD_ALL (9997)允许访问所有存储(Android 11+)。

5.2 FUSE 的权限过滤

sdcards 在用户空间实现权限过滤。

源码逻辑

// system/core/sdcard/sdcard.cpp
static int handle_lookup(struct fuse *fuse, struct fuse_req *req, fuse_ino_t parent, const char *name) {
    // 获取调用者的 UID
    uid_t uid = fuse_req_ctx(req)->uid;

    // 检查是否有权限
    if (!has_permission(uid, parent, name, MAY_READ)) {
        fuse_reply_err(req, EACCES); // 拒绝访问
        return 0;
    }
    // ... 允许访问
}

6. 内部存储(/data)与外部存储(/storage)

6.1 内部存储(Private)

  • 路径/data/data/<package_name>/
  • 特性私有。只有应用自身(相同 UID)可以访问。
  • 加密:Android 7.0+ 默认启用 FBE (File-Based Encryption),每个应用的数据独立加密。

6.2 外部存储(Public/Shared)

  • 路径/storage/emulated/0/Android/data/<package_name>/
  • 特性共享。应用可以访问自己的目录,但在 Android 11+ 无法访问其他应用的目录(Scoped Storage)。
  • 隔离:FUSE 确保每个用户只能看到自己的目录。

6.3 Scoped Storage(分区存储)

Android 10+ 引入,限制应用对外部存储的访问。

学术定义

  • 媒体集合:应用只能访问 MediaStore API 提供的图片、视频、音频。
  • 自有目录:应用只能访问 /Android/data/<package_name>/
  • SAF (Storage Access Framework):通过系统选择器访问其他文件。

7. 关键源码深度解析

7.1 StorageManagerService 的挂载逻辑

// frameworks/base/services/core/java/com/android/server/StorageManagerService.java
private void mount(String volId) {
    // 1. 获取 Volume 信息
    VolumeInfo vol = mVolumes.get(volId);

    // 2. 调用 Vold 挂载
    mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);

    // 3. 如果是 emulated,启动 FUSE
    if (vol.type == VolumeInfo.TYPE_EMULATED) {
        startFuse(vol);
    }
}

7.2 FUSE 挂载命令

# 实际的 FUSE 挂载命令
/sbin/sdcard \
  -u 1023 \  # uid
  -g 1023 \  # gid
  -m 0775 \  # 权限
  /data/media /storage/emulated

8. 存储系统的常见误区

误区学术解释
/sdcard 是真实存在的它是 FUSE 挂载点,物理数据在 /data/media
应用可以随意访问 SD 卡Android 10+ 受 Scoped Storage 限制,不能随意访问。
卸载 SD 卡就是拔出来必须先通过 Vold 卸载文件系统,否则会导致数据损坏。
内部存储比外部存储快是的。内部存储通常使用 UFS/F2FS,外部 SD 卡使用较慢的 Flash。

9. 本篇总结(Knowledge Closure)

关键点纯学术定义
Vold 的本质存储设备外交官,通过 Netlink 监听内核 uevent。
FUSE 的本质用户空间伪装者,实现多用户存储隔离和权限控制。
存储沙箱路径重定向 + GID 访问控制,通过 FUSE 实现。
Emulated Storage基于 FUSE 的虚拟存储,物理数据位于 /data/media
Scoped Storage分区存储,限制应用对外部存储的随意访问。

10. 第七板块结语

至此,第七板块:Android 存储体系与文件系统 已全部完结。

我们从 Vold 的存储管理 出发,深入 FUSE 的虚拟挂载机制,探索 多用户存储隔离,最终抵达 内部存储的加密与外部存储的沙箱

我们揭示了 Android 存储系统的设计哲学:用虚拟层隔离用户,用加密保护隐私,用权限控制访问。

下一篇预告第八板块:Android 网络体系与连接管理 | 第二十二篇:ConnectivityService 与 Netd 网络架构