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

第七板块: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 的文件系统之上,构建一套安全、隔离、多用户的存储沙箱。全程无存储优化技巧、无文件读写指南,仅保留 Android 存储体系的底层定义与系统级调度规范。


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 从物理介质到应用沙箱

#mermaid-svg-vmpKa4d72Ji7AO4X{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vmpKa4d72Ji7AO4X .error-icon{fill:#552222;}#mermaid-svg-vmpKa4d72Ji7AO4X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vmpKa4d72Ji7AO4X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vmpKa4d72Ji7AO4X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vmpKa4d72Ji7AO4X .marker.cross{stroke:#333333;}#mermaid-svg-vmpKa4d72Ji7AO4X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vmpKa4d72Ji7AO4X p{margin:0;}#mermaid-svg-vmpKa4d72Ji7AO4X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster-label text{fill:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster-label span{color:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster-label span p{background-color:transparent;}#mermaid-svg-vmpKa4d72Ji7AO4X .label text,#mermaid-svg-vmpKa4d72Ji7AO4X span{fill:#333;color:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X .node rect,#mermaid-svg-vmpKa4d72Ji7AO4X .node circle,#mermaid-svg-vmpKa4d72Ji7AO4X .node ellipse,#mermaid-svg-vmpKa4d72Ji7AO4X .node polygon,#mermaid-svg-vmpKa4d72Ji7AO4X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vmpKa4d72Ji7AO4X .rough-node .label text,#mermaid-svg-vmpKa4d72Ji7AO4X .node .label text,#mermaid-svg-vmpKa4d72Ji7AO4X .image-shape .label,#mermaid-svg-vmpKa4d72Ji7AO4X .icon-shape .label{text-anchor:middle;}#mermaid-svg-vmpKa4d72Ji7AO4X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vmpKa4d72Ji7AO4X .rough-node .label,#mermaid-svg-vmpKa4d72Ji7AO4X .node .label,#mermaid-svg-vmpKa4d72Ji7AO4X .image-shape .label,#mermaid-svg-vmpKa4d72Ji7AO4X .icon-shape .label{text-align:center;}#mermaid-svg-vmpKa4d72Ji7AO4X .node.clickable{cursor:pointer;}#mermaid-svg-vmpKa4d72Ji7AO4X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vmpKa4d72Ji7AO4X .arrowheadPath{fill:#333333;}#mermaid-svg-vmpKa4d72Ji7AO4X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vmpKa4d72Ji7AO4X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vmpKa4d72Ji7AO4X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vmpKa4d72Ji7AO4X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vmpKa4d72Ji7AO4X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vmpKa4d72Ji7AO4X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster text{fill:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X .cluster span{color:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vmpKa4d72Ji7AO4X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vmpKa4d72Ji7AO4X rect.text{fill:none;stroke-width:0;}#mermaid-svg-vmpKa4d72Ji7AO4X .icon-shape,#mermaid-svg-vmpKa4d72Ji7AO4X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vmpKa4d72Ji7AO4X .icon-shape p,#mermaid-svg-vmpKa4d72Ji7AO4X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vmpKa4d72Ji7AO4X .icon-shape .label rect,#mermaid-svg-vmpKa4d72Ji7AO4X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vmpKa4d72Ji7AO4X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vmpKa4d72Ji7AO4X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vmpKa4d72Ji7AO4X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 挂载点
用户空间
Linux 内核
物理介质
挂载
FUSE 挂载
挂载
管理
启动
分配配额
访问文件
eMMC / UFS 闪存
SD 卡 / TF 卡
USB OTG 设备
块设备层 (Block Layer)
文件系统 (ext4 / F2FS)
虚拟文件系统 (VFS)
Vold (Volume Daemon)
FUSE Daemon (sdcardd)
PackageManagerService
应用进程
/data (内部存储)
/storage/emulated (虚拟存储)
/mnt/media_rw (原始存储)

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
#mermaid-svg-WFRsocXLewMgrXLZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WFRsocXLewMgrXLZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WFRsocXLewMgrXLZ .error-icon{fill:#552222;}#mermaid-svg-WFRsocXLewMgrXLZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WFRsocXLewMgrXLZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WFRsocXLewMgrXLZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ .marker.cross{stroke:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WFRsocXLewMgrXLZ p{margin:0;}#mermaid-svg-WFRsocXLewMgrXLZ defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-WFRsocXLewMgrXLZ g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-WFRsocXLewMgrXLZ g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-WFRsocXLewMgrXLZ g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-WFRsocXLewMgrXLZ g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-WFRsocXLewMgrXLZ .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-WFRsocXLewMgrXLZ .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-WFRsocXLewMgrXLZ .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-WFRsocXLewMgrXLZ .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-WFRsocXLewMgrXLZ .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-WFRsocXLewMgrXLZ .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-WFRsocXLewMgrXLZ .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-WFRsocXLewMgrXLZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WFRsocXLewMgrXLZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WFRsocXLewMgrXLZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WFRsocXLewMgrXLZ .edgeLabel .label text{fill:#333;}#mermaid-svg-WFRsocXLewMgrXLZ .label div .edgeLabel{color:#333;}#mermaid-svg-WFRsocXLewMgrXLZ .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-WFRsocXLewMgrXLZ .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-WFRsocXLewMgrXLZ .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-WFRsocXLewMgrXLZ .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WFRsocXLewMgrXLZ .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WFRsocXLewMgrXLZ #statediagram-barbEnd{fill:#333333;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WFRsocXLewMgrXLZ .cluster-label,#mermaid-svg-WFRsocXLewMgrXLZ .nodeLabel{color:#131300;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-WFRsocXLewMgrXLZ .note-edge{stroke-dasharray:5;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-note text{fill:black;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram-note .nodeLabel{color:black;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagram .edgeLabel{color:red;}#mermaid-svg-WFRsocXLewMgrXLZ #dependencyStart,#mermaid-svg-WFRsocXLewMgrXLZ #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-WFRsocXLewMgrXLZ .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WFRsocXLewMgrXLZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 设备插入
开始检查
检查通过,挂载
用户请求卸载
卸载完成
物理拔出
消失
Unmounted
Checking
Mounted
Unmounting
Ejecting

3.3 关键源码解析

cpp 复制代码
// 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 内核模块,再返回给应用。

/data/media/0 sdcardd (FUSE Daemon) FUSE 内核 Kernel VFS 应用 (UID 10001) /data/media/0 sdcardd (FUSE Daemon) FUSE 内核 Kernel VFS 应用 (UID 10001) #mermaid-svg-ZAYf8VCzITjUKtcM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAYf8VCzITjUKtcM .error-icon{fill:#552222;}#mermaid-svg-ZAYf8VCzITjUKtcM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZAYf8VCzITjUKtcM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZAYf8VCzITjUKtcM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZAYf8VCzITjUKtcM .marker.cross{stroke:#333333;}#mermaid-svg-ZAYf8VCzITjUKtcM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZAYf8VCzITjUKtcM p{margin:0;}#mermaid-svg-ZAYf8VCzITjUKtcM .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZAYf8VCzITjUKtcM text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZAYf8VCzITjUKtcM .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZAYf8VCzITjUKtcM .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZAYf8VCzITjUKtcM #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZAYf8VCzITjUKtcM .sequenceNumber{fill:white;}#mermaid-svg-ZAYf8VCzITjUKtcM #sequencenumber{fill:#333;}#mermaid-svg-ZAYf8VCzITjUKtcM #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZAYf8VCzITjUKtcM .messageText{fill:#333;stroke:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZAYf8VCzITjUKtcM .labelText,#mermaid-svg-ZAYf8VCzITjUKtcM .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .loopText,#mermaid-svg-ZAYf8VCzITjUKtcM .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZAYf8VCzITjUKtcM .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZAYf8VCzITjUKtcM .noteText,#mermaid-svg-ZAYf8VCzITjUKtcM .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ZAYf8VCzITjUKtcM .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZAYf8VCzITjUKtcM .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZAYf8VCzITjUKtcM .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZAYf8VCzITjUKtcM .actorPopupMenu{position:absolute;}#mermaid-svg-ZAYf8VCzITjUKtcM .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZAYf8VCzITjUKtcM .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZAYf8VCzITjUKtcM .actor-man circle,#mermaid-svg-ZAYf8VCzITjUKtcM line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZAYf8VCzITjUKtcM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open("/storage/emulated/0/file.txt") 转发请求 读取请求 检查 UID 10001 是否有权限 访问 /data/media/0/file.txt 文件内容 返回内容 返回内容 文件句柄


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 在用户空间实现权限过滤。

源码逻辑

cpp 复制代码
// 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 的挂载逻辑

java 复制代码
// 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 挂载命令

bash 复制代码
# 实际的 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 网络架构

相关推荐
小短腿的代码世界2 小时前
Qt Quick 3D场景导入与渲染架构深度解析:从USD到PBR材质的完整管线
qt·3d·架构
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章24:adoop工业应用总结与展望 - 技术路线图与最佳实践
大数据·人工智能·hadoop·分布式·学习·架构·高炉炼铁
小短腿的代码世界2 小时前
Qt文本布局引擎深度解析:从QTextDocument排版到渲染的完整架构
开发语言·qt·架构
heimeiyingwang2 小时前
【架构实战】注册中心选型:Nacos vs Eureka vs Consul
微服务·云原生·架构
唯刻V3 小时前
谷歌官方 Android CLI 深度解读
android·cli·ai开发·ai时代·android cli
aidou13143 小时前
Kotlin中自定义RadioGroup实现多个RadioButton自动换行
android·开发语言·kotlin·shape·radiobutton·selector·radiogroup
小二·3 小时前
MySQL 8.0 性能优化与索引原理
android·mysql·性能优化
feifeigo1233 小时前
C# ADB 安卓设备数据传输工具
android·adb·c#
飞猿_SIR3 小时前
RK3288 Android11平台移植RTL8733BU-WiFi模组
android·嵌入式硬件