6、动态分区刷写流程:fastboot flash super 背后发生了什么?
说实话,很多做Android移植的工程师,对 fastboot flash super 这条命令都不陌生。但你要问他背后到底干了啥,十有八九答不上来。我当年刚接触动态分区时也一样,觉得不就是个刷写命令嘛,能有多复杂?直到有一次在项目里把super分区刷坏了,整台设备变砖,我才老老实实去啃了源码。
今天我们就来扒一扒,这条命令背后到底发生了什么。
6.1 从用户输入到协议传输
你在终端敲下 fastboot flash super super.img,首先启动的是PC端的fastboot工具。它会做这几件事:
- 解析命令行参数,识别出要刷写的分区名是
super - 打开
super.img文件,读取文件头信息 - 通过USB(或网络)与设备建立fastboot协议连接
- 发送
DOWNLOAD指令,把镜像数据一块块传过去
这里有个细节我提一下。fastboot协议其实挺原始的,它没有复杂的握手机制,就是简单的「发一块,等ACK,再发下一块」。默认每块大小是 MAX_DOWNLOAD_SIZE,通常是256KB或1MB。我在项目中遇到过,如果镜像特别大(比如super分区有8GB),传输过程会非常慢。后来我们改用了稀疏镜像格式,才把传输时间降下来。
fastboot flash -S 1M super super.img 可以强制分片传输,有时候能快不少。
6.2 设备端接收与校验
设备端收到数据后,bootloader(通常是ABL或U-Boot)会做几件事:
- 校验数据完整性:检查CRC或哈希值,确保传输没出错
- 写入临时缓冲区:数据先存到内存里,不会直接写进闪存
- 等待完整镜像:直到所有数据块都收完,才开始下一步
嗯,这里要注意。bootloader的内存缓冲区是有限的。如果super.img太大,缓冲区装不下,就会触发分段写入机制。我记得有一次调试,发现设备总是在传输到70%左右时重启,查了半天才发现是内存不足导致的。后来我们调整了 CONFIG_FASTBOOT_BUF_SIZE 才解决。
6.3 解析super分区布局
完整镜像收完后,bootloader开始解析super.img的头部信息。super镜像的格式是Android定义的,结构大致如下:
| 超级头部 (super_header) |
| 分区表 (partition_table) |
| 各子分区的镜像数据 |
超级头部里包含了魔法数、版本号、分区表偏移量、逻辑块大小等关键信息。分区表则记录了每个子分区的名称、大小、属性和在super镜像中的偏移位置。
你想想看,bootloader拿到这些信息后,就能知道:
- 这个super分区里包含了哪些子分区(system、vendor、product等)
- 每个子分区应该有多大
- 每个子分区的数据在镜像文件里的什么位置
我个人习惯在解析这一步加一些日志打印,方便调试时确认分区布局是否正确。比如:
// 伪代码示意
for (int i = 0; i < partition_count; i++) {
printf("分区[%d]: %s, 偏移: %llu, 大小: %llu\n",
i, partition_table[i].name,
partition_table[i].offset,
partition_table[i].size);
}
6.4 写入物理分区
解析完成后,真正的刷写开始了。bootloader会按照分区表的描述,把每个子分区的数据写入到物理闪存中。这里涉及到一个关键概念——物理分区 vs 逻辑分区。
动态分区技术把super分区看作一个「容器」,里面可以动态创建和调整逻辑分区。但在刷写阶段,bootloader是直接操作物理分区的。它会:
- 找到super分区对应的物理块设备(比如
/dev/block/by-name/super) - 按照分区表的偏移量,把每个子分区的数据写入对应位置
- 写入完成后,更新分区元数据(比如
lp_metadata)
6.5 更新slot信息
如果你的设备支持A/B分区(现在大部分新设备都支持),刷写完成后还要更新slot信息。bootloader会:
- 标记当前刷写的slot为
successful或unbootable - 更新
misc分区中的启动控制信息 - 如果刷写失败,自动回退到另一个slot
我曾经在调试OTA升级时遇到过一个问题:刷完super分区后,设备总是从旧slot启动。查了半天才发现是slot标记没更新,bootloader认为新slot不可用。后来我们在刷写流程里加了一步强制激活新slot的逻辑,问题才解决。
6.6 整体流程可视化
说了这么多,我画了一张流程图,帮你把整个流程串起来:
6.7 几个容易踩的坑
最后,我分享几个实际项目中遇到的坑,希望能帮你少走弯路:
坑1:镜像大小与分区大小不匹配
有一次我刷写super分区,明明镜像只有2GB,但分区表里写的是4GB。结果刷完后系统启动不了,因为bootloader按照分区表的大小去校验,发现后面全是空数据,直接报错。后来我养成了习惯,刷写前先用 fastboot getvar partition-size:super 确认一下分区大小。
坑2:稀疏镜像的兼容性问题
稀疏镜像虽然能减小传输体积,但有些老版本的bootloader不支持。我遇到过一台设备,刷稀疏镜像时总是卡在99%,换成非稀疏镜像就一次过。如果你也遇到类似问题,可以试试 img2simg 和 simg2img 工具转换格式。
坑3:A/B slot切换失败
这个前面提过。刷完super分区后,一定要确认slot标记是否正确。我建议在刷写脚本里加上 fastboot set_active other 或类似的命令,强制激活新slot。
好了,关于 fastboot flash super 背后的流程,今天就聊到这里。说白了,它就是一个「传输→解析→写入→更新」的流水线。每个环节都有它的设计考量,也都有潜在的坑。理解了这些,你以后遇到刷写问题,就知道该从哪里入手排查了。
公众号:蓝海资料掘金营,微信deep3321