找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3428

积分

0

好友

480

主题
发表于 2026-2-11 06:45:43 | 查看: 33| 回复: 0

嵌入式系统 OTA 固件升级文章中,我们探讨了其必要性与核心机制。其中,A/B分区方案作为实现高可靠性、无缝升级和安全回滚的关键技术被反复提及。它有效解决了传统单分区升级中可能出现的“变砖”风险,显著提升了设备的健壮性与用户体验。

然而,理解原理只是第一步。如何在实际项目中落地A/B分区,特别是如何利用Bootloader(如U-Boot)进行分区切换,才是工程师面临的核心挑战。本文旨在聚焦实战,详细阐述如何设计分区表、配置U-Boot环境变量,并编写Linux层的切换逻辑,从而构建一套稳定可靠的A/B升级系统。我们将以U-Boot作为典型引导程序进行讲解,提供一份可直接参考的操作指南。本文是对相关理论知识的概述,更深入的内容可参考瑞芯微、NXP等SoC厂商的相关文档。欢迎在云栈社区交流更多嵌入式开发经验。

1. 分区表设计

实现A/B分区的第一步,是合理规划设备的存储布局。一个典型的A/B分区方案通常包含以下几个关键分区:

分区名称 描述
bootloader 存放U-Boot镜像,通常为只读,不轻易升级。
bootloader_env 存放U-Boot的环境变量,A/B切换的核心逻辑所在。
boot_a / boot_b 存放A/B两套内核镜像(如zImage)。
rootfs_a / rootfs_b 存放A/B两套根文件系统。
data 存放用户数据与应用配置,该分区在A/B系统间共享,升级时不被擦除。

设计要点:

  • boot_a 与 boot_b、rootfs_a 与 rootfs_b 的大小必须完全一致。
  • bootloader_env 分区的大小需根据U-Boot配置确定,通常为128KB或256KB。

2. 三层协同机制

在深入细节前,我们先理清整个系统架构的层次关系。A/B分区的实现依赖于以下三个层次的协同工作:

┌─────────────────────────────────────────┐
│  应用层(Linux 用户空间)                │  ← 触发更新、确认启动
│  - OTA 更新程序                         │
│  - 启动确认服务                         │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│  引导层(U-Boot)                       │  ← 切换分区、自动回滚
│  - 环境变量管理                         │
│  - bootcmd 启动脚本                     │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│  存储层(eMMC 分区)                    │  ← 物理存储
│  - boot_a / boot_b                      │
│  - rootfs_a / rootfs_b                  │
│  - data(共享数据)                     │
└─────────────────────────────────────────┘
  • 应用层:负责下载固件、写入非活动分区、触发更新标志。
  • 引导层:负责读取更新标志、切换启动分区、处理启动失败。
  • 存储层:提供物理隔离,保证两个分区相互独立。

3. U-Boot环境变量:A/B切换的“开关”

U-Boot的环境变量是实现A/B切换逻辑的核心。我们通过几个关键变量来控制系统的启动流程,这涉及到对系统底层内存管理的精细操作。

核心环境变量设计:

  • slot_active:标记当前哪个分区是活动分区(ab)。
  • slot_updated:标记非活动分区是否刚刚被更新(10)。当OTA更新完成后,应用层会将其置为1
  • boot_count:启动计数器。每次尝试从新分区启动时,该值递减。如果减到0仍启动失败,则判定新分区有问题,触发自动回滚。

bootcmd启动脚本逻辑:
bootcmd是U-Boot自动执行的命令,我们将在这里实现A/B切换的完整逻辑。其流程图如下:

启动 U-Boot
     ↓
检查 slot_updated 标志
     ↓
   是否为 1?
     ├─→ 是:切换 slot_active,设置 boot_count=3,清除 slot_updated
     └─→ 否:继续
     ↓
检查 boot_count 是否存在
     ↓
   是否存在?
     ├─→ 是:减 1 并保存
     │        ├─→ boot_count > 0:继续启动
     │        └─→ boot_count = 0:回滚到另一个 slot,重启
     └─→ 否:正常启动(没有重试机制)
     ↓
根据 slot_active 加载对应分区的内核和设备树
     ↓
启动内核

脚本逻辑解析:

  1. 升级检测:判断slot_updated是否为1。如果是,说明OTA刚完成,需要将slot_active切换到另一分区,并初始化boot_count
  2. 启动尝试与回滚:如果boot_count存在,说明正处于“试用”新系统的阶段。每次启动都将其减一。当boot_count减至0,意味着新系统连续多次启动失败,U-Boot会自动切回原来的slot_active并重启,实现无人值守的回滚。
  3. 加载启动:根据slot_active的值,从对应的boot_a/brootfs_a/b加载并启动系统。

为什么允许3次启动尝试? 这是一个经验值。有些系统第一次启动可能因初始化问题失败,第二次就能成功。3次是一个平衡尝试次数与回滚效率的值。在U-Boot的代码中也能看到类似设计。

4. Linux应用层触发与确认

U-Boot的脚本提供了自动化的切换和回滚能力,而Linux应用层则负责在合适的时机“触发”并最终“确认”这个流程。

OTA更新流程:

  1. 下载固件:应用从服务器下载新的固件包到data分区。
  2. 安装固件:应用判断当前活动分区是A还是B,然后将新固件解压并写入到非活动分区(例如,当前是A,就写入B的boot_brootfs_b)。
  3. 触发更新:使用fw_setenv工具(U-Boot提供的用户空间工具)将slot_updated环境变量设置为1
    fw_setenv slot_updated 1
  4. 重启:执行reboot命令,U-Boot将接管后续的切换工作。

新系统启动成功后的确认:
当新系统成功启动后,需要在应用层执行“确认”操作,告知Bootloader:“新系统运行稳定,以后就固定从这个分区启动了。” 这个操作的核心是清除boot_count环境变量。

#include <stdlib.h>

void confirm_boot_successful() {
   // 检查系统是否稳定,例如网络、核心服务是否正常
   if (is_system_stable()) {
     // 系统稳定,清除 boot_count,锁定当前分区
     system("fw_setenv boot_count");
     printf("Boot successful, boot_count cleared.\n");
   } else {
     // 如果检查失败,可以直接执行 reboot
     // U-Boot 会因为 boot_count 递减而最终触发回滚
     printf("System unstable, rebooting for rollback...\n");
     system("reboot");
   }
}

这段C语言代码示例展示了如何在应用层进行启动确认,其中用到了system函数来调用shell命令,这要求开发者对程序与内核及系统环境的交互有清晰的理解。

5. 实战避坑指南

  • fw_setenv工具的配置fw_setenv需要一个配置文件/etc/fw_env.config,用于指定环境变量分区所在的设备及偏移量。此配置必须与硬件存储布局完全匹配,否则无法正确读写环境变量。
  • 环境变量的原子性:必须确保U-Boot在saveenv(保存环境变量)期间具备掉电保护机制(通常采用双备份或CRC校验)。否则,环境变量在写入中途因断电而损坏,将导致系统无法启动。
  • 共享数据分区的兼容性data分区是A/B系统共享的,必须确保新旧两个版本的系统都能兼容地读写其中的数据格式。在进行数据表结构或文件格式变更时需要格外谨慎,做好向前或向后兼容。

6. 总结

通过“分区设计 + U-Boot环境变量 + Linux应用层工具”三者的紧密结合,我们构建了一套健壮的A/B OTA升级系统。这套系统的核心优势在于,利用U-Boot的脚本能力,将复杂的启动失败检测与自动回滚逻辑下沉到Bootloader层,极大地简化了上层应用的设计复杂度。

掌握了这套实战方法,你不仅能为自己产品构建高可靠的OTA升级能力,更能对嵌入式系统的启动流程与健壮性设计有更深层次的理解。那么,新的问题来了:是否存在三备份(A/B/C)的分区方案?又如何进一步保证升级过程与固件本身的安全性呢?

低电量与满电量状态对比图




上一篇:具身智能大脑LPLM如何驱动机器人,两年拿下6亿订单实现商用落地
下一篇:计算机视觉2025学习路线:从OpenCV到视觉大模型的实战指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 10:24 , Processed in 0.922610 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表