双备份方案(A/B分区):基本原理、分区布局、启动流程

双备份方案,圈里人常叫它 A/B 分区,或者无缝升级。说白了,就是给固件准备两套完整的运行环境。一套在跑,另一套闲着。升级的时候,直接往闲的那套里写新固件,写完了重启切换过去。

我最早接触这个方案是在做车机项目的时候。客户要求升级不能黑屏,不能中断音乐播放。你想想看,传统的单分区方案根本做不到——擦写 Flash 那几秒,系统就是死的。A/B 分区就是为这种场景设计的。

基本原理

核心思想其实很简单:永远保证有一个可启动的固件副本。

系统里有两套完整的固件分区,分别叫 slot A 和 slot B。当前启动的是哪个 slot,另一个 slot 就是待更新的目标。升级时,Bootloader 把新固件写到非活跃 slot,写完后通过一个标志位告诉 Bootloader「下次启动用新的」。如果新固件启动失败,Bootloader 能自动回退到旧的 slot。

关键点:任何时候,至少有一个 slot 是已知可用的。这就是双备份方案的底气。

我见过不少团队在初期觉得双分区浪费空间,想用单分区加压缩的方式省 Flash。结果呢?升级过程中断电,设备变砖,售后成本远高于那几 MB 的 Flash 差价。嗯,这里要注意,空间换可靠性,在固件升级这个领域是铁律。

分区布局

一个典型的 A/B 分区布局长这样:

分区名称 起始地址 大小 说明
Bootloader 0x0000_0000 64 KB 引导加载程序,不可升级
Slot A 0x0001_0000 2 MB 固件副本 A
Slot B 0x0021_0000 2 MB 固件副本 B
Metadata 0x0041_0000 4 KB 启动状态、版本号、CRC
User Data 0x0041_1000 剩余空间 用户配置、日志等

这里我特别想强调 Metadata 分区。别看它只有 4KB,它才是整个方案的灵魂。里面存了什么?

  • 当前活跃 slot 编号:0 表示 slot A,1 表示 slot B
  • 尝试计数:新固件启动失败后,Bootloader 靠这个决定是否回退
  • 固件 CRC 校验值:启动前验证固件完整性
  • 升级状态标志:标记升级是否完成、是否正在升级中

我曾经踩过的坑:Metadata 分区一定要用独立的擦写策略。别跟固件分区共用同一个擦除块。否则升级过程中 Metadata 被意外擦掉,Bootloader 就不知道启动哪个 slot 了。我建议 Metadata 单独放在一个 4KB 的 sector 里,并且每次写入前先读回验证。

启动流程

整个启动流程,我习惯把它拆成四个阶段。每个阶段 Bootloader 都要做决策。

  1. 读取 Metadata
    Bootloader 上电后第一件事,就是读 Metadata 分区。拿到当前活跃 slot 编号、尝试计数、CRC 值。
  2. 校验固件完整性
    计算目标 slot 的固件 CRC,跟 Metadata 里存的值比对。不一致?直接切到另一个 slot。
  3. 尝试启动
    跳转到固件入口。同时把尝试计数加 1,写回 Metadata。这一步很关键——如果新固件启动后崩溃重启,计数已经增加了。
  4. 固件确认
    新固件正常运行后,主动调用一个确认接口,告诉 Bootloader「我跑起来了」。Bootloader 收到确认后,把尝试计数清零。

为什么会设计尝试计数?我解释一下。假设新固件有 bug,启动到一半就挂了。系统复位后,Bootloader 发现尝试计数已经变成 1,超过阈值(通常是 3),就判定这个 slot 不可用,自动切回旧 slot。这就是回滚的触发机制。

个人经验:尝试计数的阈值别设太大。3 次足够了。设成 10 次,意味着设备要反复重启 10 次才回滚,用户体验极差。我一般设 3 次,配合 5 秒的看门狗超时,15 秒内就能完成回滚决策。

下面这张图把整个流程串起来了:

A/B 分区启动与回滚流程 A/B 分区启动与回滚流程 系统上电 读取 Metadata 分区 校验目标 slot CRC CRC 通过? 是 否 尝试计数 +1,启动固件 切换到另一个 slot 回到 CRC 校验 固件确认,计数清零 说明:尝试计数超过阈值(默认 3 次)时,Bootloader 自动判定当前 slot 不可用,切换到另一个 slot

流程里有个细节容易被忽略:固件确认的时机。我见过有人把确认放在 main 函数的第一行。这不对。万一 main 函数之前就崩了呢?正确的做法是:等系统所有关键初始化完成,外设自检通过,再调用确认。我一般放在任务调度器启动之后,确保 RTOS 已经跑起来了。

总结一下双备份方案的核心优势:

  • 升级过程中设备功能不中断(无缝升级)
  • 升级失败自动回滚,不会变砖
  • 可以在后台下载固件,用户无感知

当然,代价也很明显——Flash 空间翻倍。对于 Flash 紧张的项目,这是个不小的负担。但话说回来,现在 Flash 越来越便宜,用一倍的空间换一个「永不砖机」的承诺,我个人觉得值。