25、OTA升级与super_empty:空super分区、首次刷写

聊到OTA升级,很多人第一反应就是“差分”、“增量”、“AB分区”。这些当然重要。但有个东西,平时不怎么露面,一旦你第一次刷机、或者设备出厂时,它就会跳出来刷存在感——super_empty

说白了,super_empty就是一个“空壳子”。它里面没有任何用户数据,也没有系统镜像。它的作用,就是告诉系统:super分区应该长什么样

我记得刚接触动态分区那会儿,第一次看到super_empty这个镜像文件,我还以为是个bug。心想:这玩意儿才几百KB,能干啥?后来踩了坑才明白——它才是整个动态分区布局的“蓝图”。

为什么需要空super分区?

你想想看,动态分区和传统分区最大的区别是什么?

传统分区:每个分区(system、vendor、product)都是固定大小,写在GPT表里。你没法改,除非重新分区。

动态分区:这些分区都在super这个大容器里。super本身是一个物理分区,但里面的逻辑分区可以动态调整大小。

问题来了——第一次烧录时,super里面是空的。没有任何逻辑分区信息。系统怎么知道该创建哪些分区?每个分区该多大?

嗯,这就是super_empty登场的时候。

核心概念:super_empty 是一个“空”的super分区镜像,它只包含分区表描述(metadata),不包含任何文件系统数据。它的作用是在首次刷写时,告诉系统如何初始化super分区内的逻辑分区布局。

super_empty 的结构

super_empty 本质上就是一个metadata blob。它里面记录了:

  • super分区的总大小
  • 每个逻辑分区的名称、大小、属性
  • 分组信息(group)
  • 分区槽位信息(slot)

我习惯把它理解成“分区布局的配置文件”。只不过这个配置文件是二进制的,直接写在super分区的头部。

来看一个实际的例子。假设你的设备有a/b槽位,super分区大小为4GB,里面包含system、vendor、product三个逻辑分区。那么super_empty里记录的metadata大概长这样:

Super分区布局(示例):
- 总大小:4294967296 字节(4GB)
- 槽位数:2(a/b)
- 分组:default(所有分区都在这个组)

逻辑分区列表(槽位a):
  - system_a: 1536MB
  - vendor_a: 512MB
  - product_a: 512MB

逻辑分区列表(槽位b):
  - system_b: 1536MB
  - vendor_b: 512MB
  - product_b: 512MB

剩余空间:约 1GB(用于动态调整或保留)

注意,这里只是metadata。实际的数据(system.img、vendor.img等)是在后续的刷写步骤中才写入的。

首次刷写的完整流程

我第一次做动态分区设备的首次烧录时,犯过一个低级错误:直接往super分区里写system.img,没先写super_empty。结果设备启动后,系统根本认不出super分区里的内容——因为metadata是空的。

正确的流程应该是这样的:

  1. 烧录super_empty:先把空壳子写进super分区。这一步会初始化metadata,告诉系统“super分区里有哪些逻辑分区、每个多大”。
  2. 烧录逻辑分区镜像:把system.img、vendor.img等分别写入对应的逻辑分区。注意,这时候逻辑分区已经存在了(因为metadata里定义了),只是里面还没数据。
  3. 烧录bootloader和其他分区:boot、dtbo、vbmeta等非动态分区照常烧录。
  4. 重启设备:bootloader读取super分区里的metadata,挂载逻辑分区,启动系统。

小技巧:如果你用的是fastboot刷机,命令顺序一般是:

fastboot flash super super_empty.img
fastboot flash system system.img
fastboot flash vendor vendor.img
fastboot flash product product.img
fastboot reboot

有些工具(比如fastbootd)会自动处理metadata的更新,但手动刷写时一定要先写super_empty。

super_empty 与 OTA 的关系

你可能会问:OTA升级时,super_empty还用得上吗?

答案是:大部分情况下用不上。因为OTA升级时,super分区里已经有完整的metadata了。系统直接读取现有的metadata,然后更新逻辑分区里的内容就行。

但有一种情况例外——跨版本大升级,分区布局变了

举个例子:Android 11升级到Android 12,Google可能要求新增一个“system_ext”分区。这时候,原有的super分区布局里没有这个分区。怎么办?

OTA包里的super_empty就派上用场了。升级脚本会先读取新的super_empty,更新super分区里的metadata,然后创建新的逻辑分区,最后把数据写进去。

我曾经踩过的坑:有一次做跨版本OTA,我忘了更新super_empty,结果升级后系统找不到新增的system_ext分区,直接卡在bootloop。后来排查了半天,才发现是metadata没更新。从那以后,我每次做OTA都会检查:新版本的super_empty和旧版本的分区布局是否一致。

super_empty 的生成方式

在Android源码中,super_empty是由lpmake工具生成的。这个工具会根据设备的分区配置文件(比如dynamic_partitions_opts)生成一个空的super镜像。

命令大概长这样:

lpmake --metadata-size 65536 \
       --super-name super \
       --metadata-slots 2 \
       --device super:4294967296 \
       --group default:4294967296 \
       --partition system_a:readonly:1610612736:default \
       --partition vendor_a:readonly:536870912:default \
       --partition product_a:readonly:536870912:default \
       --partition system_b:readonly:1610612736:default \
       --partition vendor_b:readonly:536870912:default \
       --partition product_b:readonly:536870912:default \
       --sparse \
       --output super_empty.img

注意这里的--metadata-slots 2,表示保留两份metadata备份。万一第一份损坏了,还有第二份可以救急。这个设计我觉得挺靠谱的——毕竟分区表坏了,整个设备就变砖了。

SVG:首次刷写super_empty的流程

下面这张图,我画的是首次刷写时,super_empty如何引导整个流程。你可以看到,它就像一张“施工图纸”,决定了后续所有逻辑分区的布局。

首次刷写super_empty流程图 super_empty.img 空壳子(仅metadata) 烧录到super分区 初始化metadata metadata生效 逻辑分区创建完毕 烧录逻辑分区镜像 system.img → system_a vendor.img → vendor_a 数据写入逻辑分区 文件系统填充 重启设备 bootloader读取metadata 关键点 • 必须先写super_empty • metadata决定布局 • 逻辑分区按名称写入 • 重启后自动挂载 • 双槽位自动切换

实际项目中的注意事项

我在几个项目里都遇到过super_empty相关的问题,总结下来有这么几点:

  • 大小必须匹配:super_empty里定义的super分区总大小,必须和实际物理分区大小一致。否则系统会报错,拒绝挂载。
  • metadata槽位:建议保留至少2个metadata槽位。我曾经见过一个设备只留了1个槽位,结果metadata损坏后直接变砖,连救砖的机会都没有。
  • OTA升级时不要乱改:如果OTA包里的super_empty和当前设备的布局不一致,升级脚本会尝试更新metadata。但这个过程有风险——如果更新到一半断电,metadata可能损坏。所以,非必要不更新super_empty
  • 调试时可以用fastbootd:如果你在开发阶段需要手动操作逻辑分区,可以用fastboot reboot fastboot进入fastbootd模式。在这个模式下,你可以直接操作逻辑分区,而不需要关心super_empty。

我个人习惯:每次发布新版本前,我都会用lpdump工具检查一下super分区里的metadata,确保和预期一致。命令很简单:

lpdump --slot 0 /dev/block/by-name/super

这样能提前发现分区布局有没有被意外修改。

总结

super_empty虽然不起眼,但它是动态分区体系的基石。没有它,首次刷写时系统根本不知道super分区里该放什么。理解了它的作用,你就能更好地把握OTA升级的完整流程——从空壳子到完整系统,每一步都有它的影子。

嗯,下次你看到super_empty.img这个文件,别再把它当成没用的垃圾了。它可是整个动态分区布局的“总设计师”。


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