2. Android存储与分区基础

要理解 Android 动态分区与 Virtual A/B,必须先回到最底层的存储介质与分区布局。Android 设备上的所有镜像——boot、system、vendor、userdata——最终都落在同一块闪存芯片上。这块芯片是 eMMC 还是 UFS,决定了读写带宽、并发能力和随机 IO 性能;而分区表如何描述这些镜像的位置,决定了引导链能否找到它们。本节从物理介质讲到逻辑分区,为后续动态分区的“空间再分配”打基础。

一、物理介质:eMMC 与 UFS 的差异

eMMC(embedded MultiMediaCard)和 UFS(Universal Flash Storage)都是焊在主板上的闪存封装,但二者架构不同。eMMC 采用半双工并行接口,同一时刻只能读或写,命令队列深度有限;UFS 采用全双工串行接口(MIPI UniPro + M-PHY),支持命令队列(Command Queue),读写可并发,随机 IOPS 显著更高。

对 Android 而言,这个差异直接影响开机速度、应用安装耗时和 A/B 更新时的镜像写入效率。UFS 2.1 的顺序读可达约 800MB/s,而 eMMC 5.1 通常在 250~300MB/s。因此在中高端设备上,动态分区与 Virtual A/B 的收益更明显,因为快照(snapshot)和 COW 操作对随机写更敏感。

对比项eMMC 5.1UFS 2.1 / 3.1
接口并行半双工串行全双工
命令队列有限支持多队列
顺序读约 250~300MB/s约 800MB/s~2GB/s
随机读写较弱强,适合快照
典型定位入门/中端中高端/旗舰

二、GPT 分区表:分区的“地图”

Android 设备使用 GPT(GUID Partition Table)描述分区布局。GPT 位于存储起始位置,包含保护性 MBR、主 GPT 头和分区条目数组,每个分区条目记录起始 LBA、结束 LBA、分区名和 GUID。引导链(BootROM → SPL → ABL → kernel)正是通过读取 GPT 找到 boot、dtbo、vbmeta 等分区。

在传统 A/B 设备上,GPT 中会同时存在 boot_a、boot_b、system_a、system_b 等成对分区。而在动态分区方案中,GPT 不再为 system/vendor/product 单独划分物理分区,而是划分一个大的 super 分区,内部再用 LP(Logical Partition)元数据描述逻辑分区。这是理解动态分区的关键转折点。

# 查看设备分区表(需 root 或 fastboot 环境)
adb shell ls -l /dev/block/by-name/
# 典型输出片段
boot_a -> /dev/block/sda12
boot_b -> /dev/block/sda13
super  -> /dev/block/sda14
userdata -> /dev/block/sda15
vbmeta_a -> /dev/block/sda10
vbmeta_b -> /dev/block/sda11

三、传统分区:boot、system、vendor 等

传统 Android 分区按职责划分,每个分区承载一类镜像:

  • boot:包含 kernel 与 ramdisk(或 GKI 下的 generic ramdisk),负责早期初始化。
  • dtbo:设备树叠加层,描述具体硬件配置。
  • vbmeta:AVB(Android Verified Boot)元数据,校验各分区完整性。
  • system:Android 框架与系统应用,只读。
  • vendor:SoC 与硬件厂商驱动、HAL,只读。
  • product:产品定制应用与配置,只读。
  • userdata:用户数据,可读写,加密后挂载。
  • metadata:加密元数据与部分系统状态,可读写。

这些分区在 GPT 中各自占据连续的 LBA 区间,大小在出厂时固定。system 分区一旦划分 2GB,即使实际镜像只有 1.2GB,剩余空间也无法被 vendor 使用——这正是动态分区要解决的浪费问题。

四、只读与可写分区概念

Android 将分区按挂载属性分为只读与可写两类。只读分区(system、vendor、product、boot 等)在正常启动时以 ro 挂载,受 AVB 校验保护,任何修改都会导致校验失败。可写分区(userdata、metadata、misc 等)以 rw 挂载,承载运行时状态和用户数据。

只读分区并非物理上不可写,而是通过 dm-verity 与 AVB 在逻辑上强制只读。动态分区中的逻辑分区同样默认只读,更新时通过 COW 快照写入,而非直接改写原分区。

理解这一点对 Virtual A/B 至关重要:更新 system 时,系统不会直接覆盖 system_a,而是把变更写入 super 分区内的快照空间,待重启验证后再合并。只读属性保证了回滚能力。

五、案例与误区复盘

某中端设备采用 eMMC 5.1,system 分区固定 3GB,vendor 固定 1GB。OTA 升级时因 system 镜像增大 200MB 而失败,只能压缩镜像。若采用动态分区,super 分区内的空间可动态调整,system 可借用 vendor 的空闲空间,避免此类问题。

常见误区:认为“分区大小 = 镜像大小”。实际上传统分区大小是 GPT 中固定的 LBA 范围,镜像小于分区时剩余空间被浪费;镜像大于分区时直接无法烧录。动态分区正是为消除这种刚性约束而设计。

另一个误区是混淆“分区”与“文件系统”。分区是 GPT 描述的 LBA 区间,文件系统(ext4、f2fs、erofs)是在分区之上格式化的结构。只读分区常用 erofs 以节省空间,可写分区常用 f2fs 以优化闪存寿命。

Android 存储与分区基础结构 物理介质层 eMMC(半双工) UFS(全双工/队列) → 决定带宽与随机 IO GPT 分区表(LBA 地图) 保护 MBR GPT 头 分区条目数组 → 引导链据此定位镜像 传统分区(固定 LBA 区间) boot 只读 system 只读 vendor 只读 product 只读 userdata 可写 metadata 可写 动态分区过渡(下一阶段) super 分区(GPT 中唯一) LP 元数据 + 逻辑分区 → 空间可动态再分配

小结:eMMC/UFS 决定物理性能,GPT 决定分区寻址,传统分区按职责固定划分,只读与可写属性由 AVB 与挂载策略共同约束。这些基础概念是理解动态分区“为什么要把 system/vendor 塞进 super”以及 Virtual A/B“为什么需要快照”的前提。