3. OTA升级原理:升级流程与AB系统机制

OTA升级,说白了就是让设备自己给自己动手术。我刚开始接触这个领域时,总觉得这玩意儿挺玄乎——设备怎么能在运行的同时把自己给换了?后来拆开一看,其实核心就两件事:升级流程和分区切换。

这一章,咱们就把OTA升级的底裤扒干净。你理解了这些,后面分析日志时才能一眼看出问题出在哪个环节。

3.1 OTA升级的标准流程

一个完整的OTA升级,通常走五步。我习惯把这五步记成「下校解写切」——虽然不好听,但好记。

  1. 下载:从服务器拉取升级包
  2. 校验:确认包没被篡改或损坏
  3. 解压:把压缩的镜像释放出来
  4. 写入:把镜像刷到备用分区
  5. 切换:让系统下次从新分区启动

每一步都可能翻车。我在项目里见过最离谱的,是下载完校验通过,解压也正常,结果写入时断电了——设备直接变砖。嗯,后面我们会专门讲怎么从日志里定位这类问题。

3.1.1 下载阶段

设备通过HTTP/HTTPS或MQTT从OTA服务器拉取升级包。这里有个关键点:断点续传。

我曾经遇到一个案子,设备在弱网环境下反复下载失败。日志里全是「Download interrupted at 47%」。后来发现是下载模块没实现断点续传,每次失败都得从头下。你想想看,一个100MB的包,在信号不好的地方,永远下不完。

我的建议:下载阶段一定要记录进度百分比和失败时的偏移量。日志里如果只有「Download failed」没有具体位置,排查起来非常痛苦。

3.1.2 校验阶段

校验通常用MD5、SHA256或者签名验证。设备会把下载好的包算一遍哈希,跟服务器下发的哈希值对比。

这里有个坑:校验时机。有些方案是下载完才校验,有些是边下边校验。边下边校的好处是能提前发现坏包,但实现复杂一些。

注意:校验失败不一定就是包坏了。我遇到过因为设备本地时间不准,导致证书验证失败的情况。日志里报「Signature verification failed」,折腾了两天才发现是RTC电池没电了。

3.1.3 解压阶段

升级包通常是压缩的——gzip、lzma或者差分包。解压需要内存和CPU。嵌入式设备资源有限,这里容易出问题。

我记得有个项目,解压时总是报「Out of memory」。查了半天,发现是解压算法需要的内存峰值超过了设备可用内存。后来换了流式解压,边解边写,才搞定。

3.1.4 写入阶段

把解压后的镜像写入备用分区。这一步最危险,因为涉及擦写Flash。

Flash写入有几个特点:

  • 写入前必须先擦除
  • 擦除是按块(Block)进行的
  • 写入过程中断电会丢数据

我见过最惨的一次,写入到90%时电源被拔了。重启后系统发现两个分区都坏了——主分区是旧的但被标记为脏,备用分区只写了一半。这就是典型的「双分区全废」场景。

3.1.5 切换阶段

写入完成后,系统会修改启动标志,让下次启动加载新分区。这一步通常很快,但决定成败。

切换策略有两种:

  • 立即切换:写入完直接重启进新分区
  • 延迟切换:标记新分区,下次冷启动时生效

我个人更倾向延迟切换。为什么呢?因为立即切换如果新系统起不来,设备就彻底失联了。延迟切换至少还能远程回滚。

3.2 分区表与AB系统机制

AB系统,说白了就是准备两套系统分区。A分区跑着,B分区等着。升级时刷B,刷完切到B启动。这样就算B起不来,还能回A。

分区表就是告诉系统:你的A区在哪、B区在哪、数据区在哪。我习惯把分区表理解成一张「房产证」——每个分区的地址、大小、用途都写得清清楚楚。

3.2.1 典型AB分区布局

分区名称 起始地址 大小 用途
boot_a 0x00000000 2MB A系统内核
boot_b 0x00200000 2MB B系统内核
system_a 0x00400000 64MB A系统文件
system_b 0x04400000 64MB B系统文件
data 0x08400000 128MB 用户数据
misc 0x10400000 1MB 启动控制信息

misc分区特别重要。它里面存着当前应该启动哪个分区、升级状态、回滚次数等信息。我见过不少故障,就是因为misc分区被写坏了,系统不知道往哪启动。

3.2.2 AB系统的启动逻辑

系统启动时,Bootloader会读misc分区,判断该启动A还是B。逻辑其实很简单:

if (misc.boot_partition == PARTITION_A) {
    try_boot(PARTITION_A);
    if (boot_success) {
        // 正常启动
        return;
    } else {
        // A启动失败,切到B
        misc.boot_partition = PARTITION_B;
        try_boot(PARTITION_B);
    }
} else {
    // 类似逻辑处理B分区
}

这里有个关键机制:启动计数。系统启动后会递增一个计数器,如果用户正常使用,计数器会清零。如果系统反复重启,计数器一直增加,超过阈值就触发回滚。

核心要点:AB系统的精髓不是「永不失败」,而是「失败后可恢复」。日志里如果看到「Rollback triggered by boot count」,说明系统已经自动回滚了——这是好事,至少设备还能用。

3.3 AB系统升级流程全景图

下面这张图,把整个AB升级流程串起来了。我建议你多看几遍,后面分析日志时,脑子里要有这张图。

AB系统OTA升级流程图 AB系统OTA升级完整流程 运行中(A分区) 下载升级包 校验升级包 校验失败→报错 解压镜像 写入B分区 标记B分区为启动分区 重启进入B分区 关键说明 • 升级期间A分区正常运行 • 任何一步失败都不影响A • B分区启动失败自动回滚A 日志关注点 • 下载进度/断点位置 • 校验算法与结果 • 写入偏移量与校验 • 切换标记与启动计数

3.4 常见失败场景与日志特征

有了上面的基础,咱们来看看实际项目中常见的翻车场景。我整理了一个表格,方便你对照日志快速定位。

失败阶段 典型日志关键词 可能原因 我见过的案例
下载 Download failed, Timeout, HTTP 404 网络不稳定、服务器地址错误 设备在工厂内网,OTA服务器配成了外网域名
校验 Checksum mismatch, Signature invalid 包损坏、证书过期、哈希算法不匹配 服务器更新了哈希算法,设备端没同步
解压 Decompress failed, Out of memory 内存不足、压缩包格式错误 差分包基版本不对,解出来全是乱码
写入 Write error, Flash erase failed Flash坏块、写入超时、电源不稳 某批次Flash有坏块,写入到固定地址就挂
切换 Boot count exceeded, Rollback triggered 新系统启动失败、misc分区损坏 新内核驱动不兼容硬件,反复重启后回滚
避坑指南:我曾经遇到一个案子,日志里显示「Write successful」,但重启后系统还是旧的。查了两天,发现是写入时虽然返回成功,但数据实际写到了错误的分区偏移地址——因为分区表被意外修改了。所以,写入成功不代表写对了位置。日志里一定要记录写入的目标分区和偏移量。

3.5 小结

OTA升级的核心就两件事:流程要完整,AB要可靠。流程上,下载、校验、解压、写入、切换,一步都不能省。AB机制上,关键是要保证「失败了还能回来」。

你分析日志时,先看升级卡在哪一步,再看那一步的日志有没有异常。如果看到「Rollback」相关的关键词,别慌——那是AB系统在保护你的设备。