第四章:AB系统升级原理
AB系统升级,说白了就是让设备拥有两套系统分区。一套在跑,一套待命。我刚开始接触这个机制时,觉得它挺绕的。但搞懂了之后,你会发现它其实很巧妙。
4.1 AB Slot机制
AB系统把存储空间分成两个槽位:Slot A 和 Slot B。每个槽位都包含完整的系统分区——boot、system、vendor 等等。你想想看,这就像飞机有两个引擎,一个在工作,另一个随时准备顶上。
为什么要这么设计?
传统升级方式有个致命问题:升级过程中一旦断电或出错,设备就变砖了。我在项目中遇到过好几次这种情况,用户升级到一半手机没电了,回来就开不了机。那叫一个头疼。
AB系统完美解决了这个问题。升级时,系统在 unactive slot 上操作,active slot 继续正常运行。就算升级失败,大不了回退到原来的系统。
核心概念:
- active slot:当前正在运行的系统槽位
- unactive slot:待升级或待切换的槽位
- 两个槽位大小相同,分区布局一致
4.2 active slot 与 unactive slot 切换
切换逻辑其实不复杂。我来给你捋一捋:
- 设备启动时,bootloader 读取 misc 分区中的 slot 信息
- 确定哪个 slot 是 active,然后从它启动
- 升级时,把新系统写入 unactive slot
- 写入完成后,标记 unactive slot 为可启动
- 重启后,bootloader 切换到新的 slot
嗯,这里要注意一点。切换不是简单地把 active 和 unactive 互换。系统会记录每个 slot 的启动次数和状态。我记得有一次调试,发现设备反复在两个 slot 之间跳来跳去,查了半天才发现是启动计数逻辑写错了。
个人经验:我建议你在切换逻辑中加入「回滚保护」。比如新 slot 启动失败超过3次,就自动切回旧 slot。这个机制救过我不少次。
4.3 boot_control HAL 接口
boot_control HAL 是连接系统和 bootloader 的桥梁。说白了,它就是一套标准接口,让上层应用能控制底层槽位切换。
来看看核心接口定义:
// hardware/libhardware/include/hardware/boot_control.h
typedef struct boot_control_module {
struct hw_module_t common;
// 获取当前 active slot
unsigned (*getActiveBootSlot)(struct boot_control_module *module);
// 设置 active slot
int (*setActiveBootSlot)(struct boot_control_module *module,
unsigned slot);
// 标记 slot 为可启动
int (*markBootSuccessful)(struct boot_control_module *module);
// 获取 slot 信息
int (*getSlotInfo)(struct boot_control_module *module,
unsigned slot,
struct boot_slot_info *info);
} boot_control_module_t;
这些接口看着简单,但实现起来有不少坑。我拿 markBootSuccessful 举个例子:
- 系统启动后,需要调用这个接口告诉 bootloader「我起来了」
- 如果没调用,bootloader 会认为这次启动失败
- 连续失败几次,就会触发回滚
你可能会问:什么时候调用最合适?我个人习惯是在系统服务启动完成后调用。太早调用,万一后面崩溃了,bootloader 还以为系统是好的。太晚调用,又可能被误判为启动失败。
避坑指南:我曾经在一个项目里,把 markBootSuccessful 放在了 init.rc 中。结果有个服务启动特别慢,bootloader 等不及就切回去了。后来我改成了在 SystemServer 启动完成后调用,问题就解决了。
4.4 实际移植中的注意事项
移植 AB 系统时,有几个关键点你得盯紧了:
| 组件 | 注意事项 |
|---|---|
| bootloader | 必须支持读取 misc 分区,理解 slot 切换逻辑 |
| 分区表 | 每个槽位要有独立的分区,大小要一致 |
| recovery | AB 系统下 recovery 可以放在 boot 分区中 |
| 升级脚本 | 要能正确识别当前 active slot,写入 unactive slot |
嗯,分区表这块最容易出问题。我见过有人把 Slot A 和 Slot B 的大小搞不一样,结果升级时数据写不进去。你想想看,这得多尴尬。
4.5 总结
AB 系统升级的核心就三点:
- 两个槽位,一个跑一个备
- 切换逻辑要可靠,带回滚保护
- boot_control HAL 接口要正确实现
搞懂了这些,你就能理解为什么现在的手机升级很少变砖了。说白了,就是 AB 系统在背后兜底。我做了这么多年移植,最深的体会就是:系统升级这件事,宁可慢一点,也要稳一点。毕竟用户的数据比什么都重要。