3. A-B分区机制概述
在传统 Android 设备上,系统分区(system、boot、vendor 等)只有一份。OTA 升级时,设备必须重启进入 Recovery 模式,把新镜像写入原分区,再重启进入新系统。这个过程中设备无法正常使用,一旦写入中断或新系统启动失败,设备就可能变砖。A-B 分区机制正是为解决这一系列问题而设计的。
一、A-B分区的设计动机
A-B 分区(也称 Seamless Update,无缝升级)的核心动机可以归纳为三点:
- 可用性:升级在后台进行,用户可继续使用设备,只需一次重启即可切换到新系统。
- 可回滚性:新系统启动失败时,Bootloader 可自动回退到旧槽位,保证设备始终可启动。
- 可验证性:新槽位在切换为活动槽位前,可先做完整性和兼容性校验,降低升级风险。
这三点的共同前提是:设备上同时存在两套可独立启动的系统副本,即“双槽位”。
二、双槽位(Slot)概念
所谓槽位,是指一组可独立启动的分区集合。Android 将系统划分为两个槽位:slot A 与 slot B。每个槽位都包含自己的一套分区,例如:
slot A: boot_a, system_a, vendor_a, dtbo_a, vbmeta_a
slot B: boot_b, system_b, vendor_b, dtbo_b, vbmeta_b
设备在任意时刻只有一个槽位处于“活动(active)”状态,另一个槽位处于“非活动(inactive)”状态。正常运行时,系统从活动槽位启动;升级时,新镜像被写入非活动槽位,写入完成并校验通过后,通过设置元数据把非活动槽位标记为下一次启动的目标,重启后即完成切换。
三、Slot元数据:谁来决定从哪个槽位启动
槽位的状态、优先级、重试次数等信息,统称为 slot 元数据。它通常保存在 misc 分区或专门的元数据分区中,由 Bootloader 和 Android 系统共同读写。核心字段包括:
| 字段 | 含义 | 典型取值 |
|---|---|---|
| slot_suffix | 当前活动槽位后缀 | _a / _b |
| priority | 槽位启动优先级 | 0~3,数值越大越优先 |
| successful | 该槽位是否已成功启动过 | yes / no |
| tries_remaining | 剩余启动尝试次数 | 整数,通常为 3~7 |
| bootable | 该槽位是否可启动 | yes / no |
Bootloader 启动时的判断逻辑大致如下:优先选择 priority 最高且 bootable=yes 的槽位;若该槽位启动失败,则递减 tries_remaining;当尝试次数耗尽仍失败时,将 successful 置为 no,并回退到另一个槽位。系统成功启动后,会将该槽位的 successful 置为 yes,并重置重试次数。
四、无缝升级的基本原理
无缝升级的完整流程可以概括为“后台写入—校验—切换—确认”四个阶段:
- 后台写入:系统在运行期间,把 OTA 包中的新镜像写入非活动槽位对应的分区。
- 校验:写入完成后,校验镜像哈希与签名,确保数据完整且来源可信。
- 切换:更新 slot 元数据,把非活动槽位的
priority提高,并设置tries_remaining,使其成为下一次启动目标。 - 确认:重启进入新槽位后,若系统正常启动,则把该槽位标记为
successful=yes;若启动失败,Bootloader 自动回退到旧槽位。
下面这张图展示了双槽位、元数据与启动切换之间的关系:
从图中可以看到,Slot A 当前是活动槽位,Slot B 是非活动槽位。OTA 在后台把新镜像写入 Slot B 后,元数据中 Slot B 的 priority 被提高,重启时 Bootloader 就会优先选择 Slot B。若 Slot B 启动成功,它成为新的活动槽位;若失败,则回退到 Slot A。
五、典型场景与常见误区
在实际项目中,A-B 分区最常见的应用场景包括:手机系统 OTA、Android TV 机顶盒升级、车载 IVI 系统升级等。这些场景的共同诉求是“升级不能影响用户使用,且必须能回滚”。
- 误以为两个槽位必须完全一致。实际上非活动槽位可以是旧版本,也可以是新版本,取决于升级阶段。
- 误以为切换槽位就是“复制数据”。切换只是修改元数据,真正的镜像写入在切换之前已完成。
- 忽略
tries_remaining的作用。若该值设置过小,新系统首次启动稍慢就可能被误判为失败而回滚。 - 认为 A-B 分区可以无限回滚。一旦旧槽位被新镜像覆盖,回滚能力就取决于是否保留了可启动的旧版本。