6、动态分区刷写流程:fastboot flash super 背后发生了什么?

说实话,很多做Android移植的工程师,对 fastboot flash super 这条命令都不陌生。但你要问他背后到底干了啥,十有八九答不上来。我当年刚接触动态分区时也一样,觉得不就是个刷写命令嘛,能有多复杂?直到有一次在项目里把super分区刷坏了,整台设备变砖,我才老老实实去啃了源码。

今天我们就来扒一扒,这条命令背后到底发生了什么。

6.1 从用户输入到协议传输

你在终端敲下 fastboot flash super super.img,首先启动的是PC端的fastboot工具。它会做这几件事:

  1. 解析命令行参数,识别出要刷写的分区名是 super
  2. 打开 super.img 文件,读取文件头信息
  3. 通过USB(或网络)与设备建立fastboot协议连接
  4. 发送 DOWNLOAD 指令,把镜像数据一块块传过去

这里有个细节我提一下。fastboot协议其实挺原始的,它没有复杂的握手机制,就是简单的「发一块,等ACK,再发下一块」。默认每块大小是 MAX_DOWNLOAD_SIZE,通常是256KB或1MB。我在项目中遇到过,如果镜像特别大(比如super分区有8GB),传输过程会非常慢。后来我们改用了稀疏镜像格式,才把传输时间降下来。

小提示: 如果你发现刷写super分区特别慢,可以检查一下镜像是否是稀疏格式。用 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是直接操作物理分区的。它会:

  1. 找到super分区对应的物理块设备(比如 /dev/block/by-name/super
  2. 按照分区表的偏移量,把每个子分区的数据写入对应位置
  3. 写入完成后,更新分区元数据(比如 lp_metadata
警告: 刷写过程中如果断电或中断,super分区会处于不一致状态。轻则系统无法启动,重则设备变砖。所以量产时一定要保证电源稳定,最好加上双备份机制。

6.5 更新slot信息

如果你的设备支持A/B分区(现在大部分新设备都支持),刷写完成后还要更新slot信息。bootloader会:

  • 标记当前刷写的slot为 successfulunbootable
  • 更新 misc 分区中的启动控制信息
  • 如果刷写失败,自动回退到另一个slot

我曾经在调试OTA升级时遇到过一个问题:刷完super分区后,设备总是从旧slot启动。查了半天才发现是slot标记没更新,bootloader认为新slot不可用。后来我们在刷写流程里加了一步强制激活新slot的逻辑,问题才解决。

6.6 整体流程可视化

说了这么多,我画了一张流程图,帮你把整个流程串起来:

fastboot flash super 刷写流程 ① 用户输入命令 ② 解析镜像文件头 ③ 通过USB传输数据 ④ 设备端接收并校验 ⑤ 解析super分区布局 ⑥ 写入物理分区 + 更新slot 关键说明 • 步骤①-③在PC端执行 • 步骤④-⑥在设备端执行 • 镜像格式支持稀疏/非稀疏 • 分区表包含所有子分区信息 • 写入前会擦除目标区域 • 支持A/B slot自动切换 常见问题: • 传输中断导致分区损坏 • 内存不足导致刷写失败 • slot标记未正确更新 • 镜像与设备不兼容

6.7 几个容易踩的坑

最后,我分享几个实际项目中遇到的坑,希望能帮你少走弯路:

坑1:镜像大小与分区大小不匹配

有一次我刷写super分区,明明镜像只有2GB,但分区表里写的是4GB。结果刷完后系统启动不了,因为bootloader按照分区表的大小去校验,发现后面全是空数据,直接报错。后来我养成了习惯,刷写前先用 fastboot getvar partition-size:super 确认一下分区大小。

坑2:稀疏镜像的兼容性问题

稀疏镜像虽然能减小传输体积,但有些老版本的bootloader不支持。我遇到过一台设备,刷稀疏镜像时总是卡在99%,换成非稀疏镜像就一次过。如果你也遇到类似问题,可以试试 img2simgsimg2img 工具转换格式。

坑3:A/B slot切换失败

这个前面提过。刷完super分区后,一定要确认slot标记是否正确。我建议在刷写脚本里加上 fastboot set_active other 或类似的命令,强制激活新slot。

好了,关于 fastboot flash super 背后的流程,今天就聊到这里。说白了,它就是一个「传输→解析→写入→更新」的流水线。每个环节都有它的设计考量,也都有潜在的坑。理解了这些,你以后遇到刷写问题,就知道该从哪里入手排查了。


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