5、动态分区镜像生成:super.img的生成流程与参数配置

好,咱们今天聊聊 super.img 是怎么来的。说实话,我刚接触动态分区那会儿,也觉得这东西挺玄乎——一个镜像文件,里面居然能塞好几个分区?后来自己动手做了一遍,才明白其实没那么复杂。

super.img 说白了,就是一个大容器。它把 system、product、vendor 这些分区统统打包在一起。你想想看,以前每个分区各玩各的,空间浪费不说,调整大小还得改分区表。现在好了,一个 super 镜像全搞定。

5.1 生成流程概览

整个生成流程,我习惯把它分成三步:

  1. 准备原料——各分区的镜像文件(system.img、product.img 等)
  2. 配置参数——告诉工具 super 分区多大、每个子分区怎么分配
  3. 执行打包——用 lpmake 工具生成最终的 super.img

嗯,这里要注意:super.img 不是直接烧录到闪存里的。它其实是一个"逻辑镜像",真正烧录的时候还要再拆开。这个我们后面会讲到。

super.img 生成流程 步骤1:准备原料 system.img / product.img 步骤2:配置参数 分区大小 / 分组 / 属性 步骤3:执行打包 lpmake 工具 super.img 注意:super.img 是逻辑镜像,烧录时需通过 update_engine 拆解 输入:各分区镜像文件 + 分区布局描述文件

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 生成后,别急着烧录。先做几件事:

  1. 检查文件大小——应该接近但不超过 device-size
  2. 用 lpdump 查看内部结构——确认分区都在
  3. 模拟挂载——用 lpunpack 把分区解出来,看看能不能挂载

注意:千万不要在 super.img 生成后手动修改它!我见过有人试图用 hex 编辑器改分区大小,结果 metadata 和实际数据对不上,系统直接变砖。

正确的做法是:修改参数,重新用 lpmake 生成。

好了,关于 super.img 的生成流程和参数配置,就聊到这儿。核心就是记住:参数要准,验证要勤,余量要留。下次咱们再深入讲讲 OTA 升级时 super 分区是怎么被更新的。


公众号:蓝海资料掘金营,微信deep3321