第七板块:Android 存储体系与文件系统 | 第二十一篇:Vold 与 FUSE 存储架构
所属板块:第七板块 — Android 存储体系与文件系统
前置知识:第二十篇中的应用签名、PMS 安全校验、Linux 文件系统(ext4/F2FS)、SELinux 安全上下文
本篇定位:这是 Android 设备数据持久化的基石。如果说内存是暂存的思绪,那么存储就是永久的记忆。本篇将彻底拆解 Vold(Volume Daemon) 的存储管理架构、FUSE(Filesystem in Userspace) 的虚拟挂载机制、多用户存储隔离(Emulated Storage)、SD 卡与 OTG 的动态挂载、StorageManagerService 的配额管理。我们将深入 Kernel VFS、Native Daemon 与 Framework 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 核心组件职责表
| 组件 | 层级 | 职责 | 学术定义 |
|---|---|---|---|
| Vold | Native | 设备管理 | 监听 uevent,格式化、挂载、卸载存储设备。 |
| FUSE | Kernel/Native | 虚拟文件系统 | 在用户空间实现文件系统逻辑,提供权限过滤。 |
| StorageManagerService | Framework | 存储服务 | 管理存储卷的生命周期,提供 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_STORAGE或WRITE_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 的用户空间守护进程。
工作流程:
- 应用调用
open("/storage/emulated/0/file.txt")。 - Kernel VFS 将请求转发给 FUSE 内核模块。
- FUSE 内核模块通过
/dev/fuse通知sdcardd。 sdcardd检查调用者的 UID 和权限。- 如果允许,访问实际的物理文件
/data/media/0/file.txt。 - 将结果返回给 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_STORAGE | AID_SDCARD_R (1028) | 允许读取 /storage/emulated。 |
WRITE_EXTERNAL_STORAGE | AID_SDCARD_RW (1015) | 允许写入 /storage/emulated。 |
MANAGE_EXTERNAL_STORAGE | AID_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+ 引入,限制应用对外部存储的访问。
学术定义:
- 媒体集合:应用只能访问
MediaStoreAPI 提供的图片、视频、音频。 - 自有目录:应用只能访问
/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 网络架构