系统更新与 OTA:OTA 升级架构、AB 分区与无缝升级、更新策略与回滚机制
各位同学,今天我们来聊聊车载系统里一个非常关键的话题——OTA 升级。说实话,在手机行业,OTA 已经是个很成熟的技术了。但在车上,情况要复杂得多。你想想看,手机升级失败大不了重启一下,或者去售后刷个机。车要是升级失败,停在半路动不了,那可是要出大事的。
所以,车载 OTA 的核心目标就两个字:安全。怎么保证安全?靠的就是我们今天要讲的 AB 分区、无缝升级和回滚机制。我个人习惯把这三者比作「双保险」——一个保升级过程,一个保升级结果。
OTA 升级架构:从云端到车端
先看整体架构。车载 OTA 不是手机那样「下载-安装-重启」这么简单。它涉及云端、车机端、甚至多个 ECU 的协同。
核心架构分层:
- 云端 OTA 平台:负责版本管理、差分包生成、策略下发、设备认证。
- 车端 OTA 客户端:负责下载、校验、安装、状态上报。
- 更新代理(Update Engine):负责执行具体的分区写入操作。
- Bootloader & Recovery:负责启动引导和恢复模式下的更新。
我在项目中遇到过一个问题:云端下发了更新包,车端也下载成功了,但安装时发现校验失败。后来排查发现,是下载过程中网络波动导致包体损坏。所以,校验机制一定要做在安装之前,这是血的教训。
下面这张图展示了 OTA 升级的完整流程:
AB 分区与无缝升级
AB 分区,说白了就是准备两套系统分区。一套叫 Slot A,一套叫 Slot B。当前运行的是 Slot A,升级时就把新系统写到 Slot B。写完之后,重启时 Bootloader 判断一下,如果 Slot B 没问题,就切过去。如果出问题了,还能切回 Slot A。
这样做的好处很明显:用户几乎感觉不到升级过程。下载和安装都在后台进行,只有最后重启那一下会中断使用。而且,就算升级失败,系统也能自动回滚,不会变砖。
我的经验:AB 分区不是简单的「复制一份」。你要确保两个分区的 fstab、init.rc 等关键文件都正确指向各自的分区。我曾经见过一个案例,开发人员只改了 system 分区,忘了改 vendor 分区,结果升级后摄像头驱动加载失败。嗯,这种低级错误其实很容易犯。
AB 分区的核心配置在 BoardConfig.mk 中:
# 启用 AB 分区
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS := \
boot \
system \
vendor \
product \
odm
# 分区大小建议保持一致
BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2147483648
BOARD_VENDORIMAGE_PARTITION_SIZE := 536870912
这里要注意,AB_OTA_PARTITIONS 列表里列出的分区,每个都要有对应的 _a 和 _b 后缀。比如 system_a 和 system_b。分区大小最好保持一致,否则升级时可能因为空间不足而失败。
更新策略:增量 vs 全量
更新策略主要分两种:全量更新和增量更新。
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 全量更新 | 简单可靠,不依赖旧版本 | 包体大,下载时间长 | 首次升级、跨版本升级 |
| 增量更新 | 包体小,下载快 | 依赖旧版本,差分算法复杂 | 小版本迭代、安全补丁 |
我个人更倾向于增量更新,毕竟车载网络环境不比家里 Wi-Fi。但增量更新有个坑:差分包的生成必须基于精确的源版本。如果用户跳版本升级,或者某个文件被修改过,差分包就打不上。
避坑指南:我曾经遇到过一个问题:增量包打不上,原因是用户 root 过车机,修改了 system 分区里的某个文件。差分工具比对时发现文件哈希不匹配,直接拒绝安装。所以,增量更新一定要做版本校验和文件完整性校验。如果校验失败,应该自动回退到全量更新。
增量更新的核心是 bsdiff 算法。Android 的 update_engine 使用 delta_generator 工具生成差分包:
# 生成差分包
delta_generator \
--out_file=/tmp/update.zip \
--partition_names=system \
--new_partition=/tmp/system_new.img \
--old_partition=/tmp/system_old.img
# 生成完整包
delta_generator \
--out_file=/tmp/full_update.zip \
--partition_names=system \
--new_partition=/tmp/system_new.img
回滚机制:最后的防线
回滚机制是 OTA 安全的最后一道防线。说白了,就是升级失败后,系统能自动恢复到升级前的状态。
AB 分区天然支持回滚。Bootloader 会维护一个 slot_suffix 变量,记录当前启动的是哪个 Slot。如果新 Slot 启动失败(比如内核 panic、关键服务 crash),Bootloader 就会切回旧 Slot。
回滚的触发条件一般包括:
- 内核启动超时:比如 30 秒内没进入 init 进程
- 关键服务崩溃:比如 surfaceflinger、audioserver 连续重启
- 用户手动触发:通过 Recovery 模式选择回滚
回滚策略配置:
// 在 BoardConfig.mk 中配置回滚次数
AB_OTA_RETRY_COUNT := 3
// 表示新 Slot 最多尝试启动 3 次
// 如果 3 次都失败,则标记为坏 Slot
这里有个细节:回滚不是无限次的。如果新 Slot 启动失败,Bootloader 会尝试重新启动它几次(比如 3 次)。如果每次都失败,就彻底标记为坏 Slot,不再尝试。这样做是为了避免「反复重启-回滚-重启」的死循环。
我记得有一次,客户反馈说车机升级后频繁重启。排查后发现,是某个硬件驱动在新版本里不兼容,导致内核启动时 panic。Bootloader 尝试了 3 次都失败,自动回滚到旧版本,问题解决。嗯,这就是回滚机制的价值所在。
总结
OTA 升级在车载系统里是个「牵一发而动全身」的活。AB 分区保证了升级过程的安全性,增量更新优化了用户体验,回滚机制则提供了最后的保障。这三者缺一不可。
最后送大家一句话:永远不要相信升级过程不会出错。设计系统时,把「出错」当作常态,把「回滚」当作默认行为。这样,你的 OTA 系统才能真正做到安全可靠。