嵌入式固件升级分区架构设计 - 第2节

双备份分区策略:A/B分区原理

双备份分区,圈里人常叫它 A/B 分区。说白了,就是给固件准备两套完整的运行环境。一套叫 Slot A,一套叫 Slot B。你想想看,这就像飞机有两个引擎,坏了一个还能飞。

我最早接触这个方案,是在做车机项目的时候。客户要求 OTA 升级不能中断任何功能,哪怕升级到一半断电了,重启后系统也得能正常工作。嗯,A/B 分区就是为这种场景设计的。

核心原理

每个 Slot 都包含完整的固件分区:bootloader、boot 分区、system 分区、vendor 分区,甚至 data 分区也可以独立。系统运行时,只有一个 Slot 是激活的,另一个处于待命状态。

关键点:两个 Slot 在物理上是完全隔离的。一个在升级,另一个照常运行。互不干扰。

我习惯用一个简单的比喻来理解:你手里有两把钥匙,一把在用,一把放保险柜。用着的这把丢了,保险柜里还有备用的。A/B 分区就是这个道理。

A/B 双备份分区结构图 Slot A(当前激活) Bootloader Boot 分区 System 分区 Vendor 分区 Slot B(待命/升级中) Bootloader Boot 分区 System 分区 Vendor 分区 两个 Slot 物理隔离,升级时写入非激活 Slot,不影响当前运行的系统

A/B分区的工作流程

流程其实不复杂,但细节很多。我拆成几个关键阶段来讲。

1. 正常启动阶段

设备上电后,bootloader 读取 misc 分区里的 slot 标记。哪个 Slot 是 active,就启动哪个。另一个 Slot 保持原样,不动。

我的习惯:在 misc 分区里多存一个启动计数。如果系统连续三次启动失败,自动切到另一个 Slot。这个机制救过我一次,后面会细说。

2. OTA 下载阶段

系统在后台下载升级包。下载完成后,校验完整性。这个阶段,用户完全无感知。我见过一些方案把下载和升级分开做,但我个人建议下载完立刻校验,别拖。

3. 升级写入阶段

这是核心环节。系统把新固件写入非激活 Slot。比如当前跑的是 Slot A,那就往 Slot B 写。写入完成后,更新 misc 分区里的 slot 标记,把 Slot B 设为 active。

// 伪代码示例:A/B 分区切换逻辑
int update_slot_switch(void) {
    // 获取当前激活的 slot
    int current_slot = get_active_slot();
    
    // 计算目标 slot(另一个)
    int target_slot = (current_slot == SLOT_A) ? SLOT_B : SLOT_A;
    
    // 写入新固件到目标 slot
    if (write_firmware_to_slot(target_slot) != 0) {
        // 写入失败,回滚
        return -1;
    }
    
    // 校验目标 slot 的完整性
    if (verify_slot_integrity(target_slot) != 0) {
        // 校验失败,回滚
        return -2;
    }
    
    // 切换激活标记
    set_active_slot(target_slot);
    
    // 设置启动计数
    set_boot_count(0);
    
    return 0;
}

4. 重启与回滚阶段

系统重启后,bootloader 看到新的 active 标记,启动新 Slot。如果新系统启动成功,应用层会发一个确认信号,把启动计数清零。如果启动失败,bootloader 检测到计数超限,自动切回旧 Slot。

我曾经踩过的坑:启动计数设得太小,比如只设了 1 次。结果新系统启动稍微慢了点,就被判定为失败,来回切换。后来我改成 3 次,配合超时机制,才稳定下来。

A/B分区的优缺点分析

任何方案都有两面性。A/B 分区不是银弹,得看场景选。

优点

  • 升级零停机:后台升级,用户无感。这是最大的卖点。
  • 安全回滚:升级失败自动切回旧版本,设备不会变砖。
  • 原子性:要么全升成功,要么保持原样。没有中间状态。
  • 支持断点续传:下载中断了,下次继续,不用从头来。

缺点

  • 存储翻倍:两个 Slot 占双倍空间。对 Flash 小的设备不友好。
  • 分区管理复杂:bootloader 要处理 slot 切换、计数、回滚逻辑。
  • 数据分区同步:如果 data 分区不在 Slot 内,升级后可能需要做数据迁移。
  • 调试困难:两个 Slot 同时存在,出问题时得排查是哪个版本。
对比维度 A/B 分区 传统单分区
升级安全性 高(自动回滚) 低(升级失败变砖)
存储占用 高(双倍空间) 低(单份空间)
升级体验 无感升级 需要重启进入恢复模式
实现复杂度 中高 低
适用场景 车机、手机、工业设备 低端 MCU、小存储设备

我的建议:如果你的设备 Flash 大于 64MB,且对可靠性要求高,优先考虑 A/B 分区。如果 Flash 只有几 MB,那就别硬上,用其他方案更合适。

嗯,A/B 分区的基本原理和流程就这些。实际项目中,你还会遇到分区大小对齐、OTA 包增量压缩、slot 标记校验等细节。这些后面章节会展开讲。