5、动态分区镜像生成:super.img的生成流程与参数配置
好,咱们今天聊聊 super.img 是怎么来的。说实话,我刚接触动态分区那会儿,也觉得这东西挺玄乎——一个镜像文件,里面居然能塞好几个分区?后来自己动手做了一遍,才明白其实没那么复杂。
super.img 说白了,就是一个大容器。它把 system、product、vendor 这些分区统统打包在一起。你想想看,以前每个分区各玩各的,空间浪费不说,调整大小还得改分区表。现在好了,一个 super 镜像全搞定。
5.1 生成流程概览
整个生成流程,我习惯把它分成三步:
- 准备原料——各分区的镜像文件(system.img、product.img 等)
- 配置参数——告诉工具 super 分区多大、每个子分区怎么分配
- 执行打包——用 lpmake 工具生成最终的 super.img
嗯,这里要注意:super.img 不是直接烧录到闪存里的。它其实是一个"逻辑镜像",真正烧录的时候还要再拆开。这个我们后面会讲到。
5.2 核心工具:lpmake
lpmake 是生成 super.img 的主力工具。它属于 Android 的 liblp 工具集,源码在 system/core/fs_mgr/liblp 目录下。
我个人习惯在编译完成后,直接去 out/host/linux-x86/bin/ 下面找这个工具。如果你是自己编译 AOSP,那肯定见过它。
来看一个典型的 lpmake 命令:
lpmake \
--device-size 8053063680 \
--metadata-size 65536 \
--metadata-slots 3 \
--group main_a:8053063680 \
--group main_b:8053063680 \
--partition system_a:readonly:2147483648:main_a \
--partition system_b:readonly:2147483648:main_b \
--partition product_a:readonly:1073741824:main_a \
--partition product_b:readonly:1073741824:main_b \
--partition vendor_a:readonly:536870912:main_a \
--partition vendor_b:readonly:536870912:main_b \
--image system_a=out/target/product/xxx/system.img \
--image product_a=out/target/product/xxx/product.img \
--image vendor_a=out/target/product/xxx/vendor.img \
--output super.img
看到没?参数虽然多,但结构很清晰。我来拆解一下每个参数的含义。
5.3 关键参数详解
| 参数 | 含义 | 我的建议 |
|---|---|---|
--device-size |
super 分区的物理大小(字节) | 一定要和分区表里的一致,否则烧录会失败 |
--metadata-size |
元数据区域大小 | 默认 65536 够用,除非你分区特别多 |
--metadata-slots |
元数据备份数量 | 建议 3 个,防止写入时断电损坏 |
--group |
分区组名:最大大小 | 一般按 slot 分组,main_a 和 main_b |
--partition |
分区名:属性:大小:所属组 | 属性用 readonly,动态分区不支持可写 |
--image |
分区名=镜像文件路径 | 路径别搞错,我吃过这个亏 |
关于 device-size 的坑
我曾经在项目里遇到过这么个事:同事把 device-size 设得比实际闪存分区小,结果 lpmake 倒是跑通了,但烧录的时候 update_engine 直接报错。查了半天才发现是 size 不匹配。
所以我的习惯是:先确认分区表里的 super 大小,再写进 lpmake 参数。可以用 fastboot getvar partition-size:super 来查。
5.4 分区组(Group)的设计逻辑
为什么要分组?说白了就是为了支持 A/B 无缝升级。
你看上面的例子,main_a 和 main_b 各占 7.5GB。但实际用的时候,system_a、product_a、vendor_a 加起来可能只有 6GB。剩下的空间干嘛?留着给 OTA 用啊!
分组的好处是:
- 空间共享——组内分区可以灵活调整大小
- 升级隔离——A 槽升级时,B 槽完全不受影响
- 回滚支持——万一升级失败,还能切回旧槽
你想想看,如果没有分组,每个分区大小写死,那 OTA 的时候想给 system 多分 100MB 都做不到。有了分组,只要组内总空间够,随便调。
5.5 元数据(Metadata)的作用
super.img 里除了分区数据,还有一块元数据区域。它记录了:
- 分区布局(每个分区在 super 里的偏移和大小)
- 分组信息
- 校验信息
这块数据非常重要。我记得有一次调试,发现系统启动时老是找不到 product 分区。后来一查,是 metadata 里记录的偏移量错了。嗯,从那以后我每次生成 super.img 都会用 lpdump 工具验证一下。
验证 metadata 的小技巧
生成 super.img 后,用这个命令查看内部结构:
lpdump super.img
它会列出所有分区、分组、大小信息。如果和你的预期不一致,赶紧回去改参数。
5.6 实际项目中的参数配置
不同设备的参数差异很大。我整理了一份常见配置参考:
| 设备类型 | super 大小 | 典型分区 | 备注 |
|---|---|---|---|
| 手机(中端) | 6~8 GB | system, product, vendor | 一般够用 |
| 手机(旗舰) | 10~16 GB | + system_ext, odm | 分区多,metadata 要留够 |
| 平板 | 8~12 GB | + product 较大 | 预装应用多 |
| 车载 | 4~6 GB | system, vendor | 功能相对固定 |
我个人建议:super 大小不要卡得太死。留 10%~20% 的余量,给未来的 OTA 升级用。你永远不知道下个版本会多出什么功能。
5.7 生成后的验证
super.img 生成后,别急着烧录。先做几件事:
- 检查文件大小——应该接近但不超过 device-size
- 用 lpdump 查看内部结构——确认分区都在
- 模拟挂载——用
lpunpack把分区解出来,看看能不能挂载
注意:千万不要在 super.img 生成后手动修改它!我见过有人试图用 hex 编辑器改分区大小,结果 metadata 和实际数据对不上,系统直接变砖。
正确的做法是:修改参数,重新用 lpmake 生成。
好了,关于 super.img 的生成流程和参数配置,就聊到这儿。核心就是记住:参数要准,验证要勤,余量要留。下次咱们再深入讲讲 OTA 升级时 super 分区是怎么被更新的。
公众号:蓝海资料掘金营,微信deep3321