4. 动态分区配置与构建
动态分区(Dynamic Partitions)把 system、vendor、product、system_ext 等只读分区从物理分区表中“解放”出来,统一装进一个名为 super 的物理分区,由 liblp 在运行时按逻辑分区元数据动态映射。因此,构建阶段的核心工作不再是“给每个分区划固定大小”,而是回答三个问题:super 要多大、逻辑分区怎么分组、元数据怎么描述。这三件事分别落在 BoardConfig.mk、分区组配置和 fstab 上。
一、核心原理:super 是容器,逻辑分区是内容
在 Virtual A/B 方案中,super 分区同时承载 A、B 两个槽的逻辑分区。设备启动时,bootloader 把 super 的元数据交给 liblp,由 dm-linear 把逻辑分区映射成 /dev/block/mapper/system 等设备节点。因此 super 的容量必须同时满足:当前槽所有逻辑分区之和、另一槽的逻辑分区之和、以及元数据与对齐开销。
super 大小计算的基本公式为:
super_size ≥ (A槽逻辑分区总和 + B槽逻辑分区总和)
+ 元数据区(metadata) + 备份元数据(backup)
+ 对齐与预留余量
其中:
元数据区 = 2 × metadata_size(主+备)
对齐余量 = 每个分区按 1MB 对齐后的向上取整损耗
预留余量 = 建议 ≥ 10%,为后续 OTA 增长留空间
如果 super 太小,OTA 会因“no space left”失败;如果 super 太大,会挤占 userdata 空间。因此它是一次容量与升级能力的权衡。
二、分区组:把逻辑分区按“更新边界”归类
分区组不是物理概念,而是 OTA 的更新边界。同一组内的逻辑分区在 OTA 时作为一个整体被快照和写入,因此组内分区必须共享相同的更新策略。常见做法是:
- qti_dynamic_partitions:承载
system、system_ext、product,随系统镜像一起更新。 - vendor_group:承载
vendor、odm、vendor_dlkm,随 vendor 镜像更新,与系统解耦。 - B 槽组:A/B 设备中,B 槽分区通常单独成组,命名带
_b后缀,便于独立快照。
分组错误会导致 OTA 时“跨组快照”,出现 snapshot merge failed 或分区大小不匹配。因此分组必须与 PRODUCT_USE_DYNAMIC_PARTITIONS、BOARD_SUPER_PARTITION_GROUPS 保持一致。
三、分区表配置:BoardConfig 与 fstab 的分工
构建配置分两层:BoardConfig.mk 决定“super 多大、有哪些组、每组上限多少”;fstab 决定“运行时怎么挂载这些逻辑分区”。两者必须参数一致,否则会出现“编译通过、开机挂载失败”。
| 配置项 | 所在文件 | 作用 | 典型值 |
|---|---|---|---|
BOARD_SUPER_PARTITION_SIZE |
BoardConfig.mk | super 物理分区总大小 | 6442450944(6 GB) |
BOARD_SUPER_PARTITION_GROUPS |
BoardConfig.mk | 声明分区组名称列表 | qti_dynamic_partitions vendor_group |
BOARD_<GROUP>_PARTITION_LIST |
BoardConfig.mk | 组内逻辑分区清单 | system system_ext product |
BOARD_<GROUP>_SIZE |
BoardConfig.mk | 该组容量上限 | 4294967296 |
BOARD_SUPER_PARTITION_METADATA_DEVICE |
BoardConfig.mk | 元数据存放设备 | super |
logical_partition 条目 |
fstab | 运行时逻辑分区挂载点 | /dev/block/mapper/system |
一个典型的 BoardConfig 片段如下:
BOARD_SUPER_PARTITION_SIZE := 6442450944
BOARD_SUPER_PARTITION_GROUPS := qti_dynamic_partitions vendor_group
BOARD_QTI_DYNAMIC_PARTITIONS_SIZE := 4294967296
BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST := \
system system_ext product
BOARD_VENDOR_GROUP_SIZE := 1610612736
BOARD_VENDOR_GROUP_PARTITION_LIST := \
vendor odm vendor_dlkm
BOARD_SUPER_PARTITION_METADATA_DEVICE := super
BOARD_SUPER_PARTITION_BLOCK_DEVICES := super
对应的 fstab 逻辑分区条目:
system /system ext4 ro,barrier=1 wait,logical,first_stage_mount
vendor /vendor ext4 ro,barrier=1 wait,logical,first_stage_mount
product /product ext4 ro,barrier=1 wait,logical,first_stage_mount
logical 标志告诉 first-stage init:该分区不是物理分区,需要先由 liblp 从 super 元数据中映射出 /dev/block/mapper/<name>,再执行挂载。四、案例与复盘:一次 super 扩容引发的 OTA 失败
某项目在 Android 13 上升级到 Android 14,新增了 vendor_dlkm 并扩大了 product。构建时只改了 BOARD_QTI_DYNAMIC_PARTITIONS_SIZE,却忘了同步 BOARD_SUPER_PARTITION_SIZE。结果:
- 编译阶段
build_super_image报super partition size too small,因为 A+B 两组之和已超过 super 容量。 - 临时把 super 调大后编译通过,但 OTA 包生成时
ota_from_target_files提示not enough space for snapshot,原因是预留余量不足 10%。 - 最终把 super 从 6 GB 调到 7 GB,并给
qti_dynamic_partitions增加 512 MB 余量,OTA 才通过。
logical 导致挂载物理分区失败;分区组命名与 BOARD_*_PARTITION_LIST 前缀不一致,导致 liblp 找不到组。复盘结论:super 大小、分区组、BoardConfig、fstab 是同一套参数在四个位置的投影,任何一处改动都必须同步校验。建议在构建脚本中加入一致性检查:sum(group_size) + metadata + reserve ≤ super_size,并在 fstab 与 BoardConfig 之间做分区名交叉比对,把问题拦截在编译阶段,而不是留到 OTA 或开机阶段。