嵌入式固件升级分区架构设计 - 第2节
双备份分区策略:A/B分区原理
双备份分区,圈里人常叫它 A/B 分区。说白了,就是给固件准备两套完整的运行环境。一套叫 Slot A,一套叫 Slot B。你想想看,这就像飞机有两个引擎,坏了一个还能飞。
我最早接触这个方案,是在做车机项目的时候。客户要求 OTA 升级不能中断任何功能,哪怕升级到一半断电了,重启后系统也得能正常工作。嗯,A/B 分区就是为这种场景设计的。
核心原理
每个 Slot 都包含完整的固件分区:bootloader、boot 分区、system 分区、vendor 分区,甚至 data 分区也可以独立。系统运行时,只有一个 Slot 是激活的,另一个处于待命状态。
关键点:两个 Slot 在物理上是完全隔离的。一个在升级,另一个照常运行。互不干扰。
我习惯用一个简单的比喻来理解:你手里有两把钥匙,一把在用,一把放保险柜。用着的这把丢了,保险柜里还有备用的。A/B 分区就是这个道理。
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 标记校验等细节。这些后面章节会展开讲。