2. 嵌入式Linux基础回顾

在讨论版本升级与维护之前,必须先把嵌入式 Linux 的“家底”理清楚。升级不是简单替换一个文件,而是对引导链、内核、设备树、根文件系统、驱动模块与用户态服务的整体协同变更。只有理解系统组成与启动顺序,才能判断升级失败时问题出在哪一环,才能设计出可回滚、可验证的维护方案。

一、系统组成:四层结构

典型的嵌入式 Linux 系统可以划分为四个层次:

  • 引导层(Bootloader):如 U-Boot、SPL、TF-A。负责初始化 DDR、时钟、串口等最小硬件环境,加载内核与设备树,传递启动参数。
  • 内核层(Kernel):提供进程调度、内存管理、文件系统、网络协议栈、驱动框架。嵌入式场景常配合设备树(Device Tree)描述板级硬件。
  • 根文件系统层(RootFS):包含 /bin、/lib、/etc、/dev 等目录,提供 init 程序、动态库、配置文件和应用程序。
  • 应用与服务层:业务程序、守护进程、升级代理、通信中间件等,通常由 systemd、BusyBox init 或自研 init 拉起。

这四层之间存在严格的版本匹配关系:内核模块与内核版本必须一致,动态库与工具链 ABI 必须兼容,设备树与硬件板型必须对应。升级维护的核心工作,就是管理这些“版本契约”。

二、启动流程:从复位向量到用户态

嵌入式 Linux 的启动流程可以概括为一条链:

上电复位
  → BootROM(固化代码,选择启动介质)
  → SPL / 二级引导(初始化 DDR)
  → U-Boot(加载 kernel + dtb + rootfs)
  → Kernel 解压、初始化、挂载根文件系统
  → 执行 init(/sbin/init 或 /linuxrc)
  → 用户态服务启动,进入业务运行

其中 U-Boot 阶段最关键,它决定了从哪里加载镜像、是否校验、是否支持 A/B 分区。常见环境变量如下:

bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait
bootcmd=mmc dev 0; fatload mmc 0:1 ${loadaddr} zImage; bootz ${loadaddr} - ${fdt_addr}

升级方案设计时,必须明确:升级的是哪一段。只升级应用,风险最小;升级内核或 Bootloader,则必须考虑断电保护和回滚机制。

三、根文件系统:只读与可写分离

根文件系统是升级中最常被替换的部分。嵌入式产品通常采用“只读根 + 可写数据分区”的策略:

  • 只读根文件系统:squashfs、erofs、只读 ext4,保证系统镜像完整、可校验、可回滚。
  • 可写数据分区:/data、/userdata,保存配置、日志、证书,升级时保留。
  • 临时文件系统:/tmp、/run 使用 tmpfs,避免频繁写 Flash。

这种分离让升级镜像可以整体替换,同时不丢失用户数据。若把配置直接写在根文件系统中,升级后配置会被覆盖,这是常见误区。

四、内核与驱动关系

内核与驱动的关系可以理解为“框架与实现”。内核提供总线、设备模型、字符/块/网络子系统;驱动以模块或内建方式接入。设备树负责描述硬件资源,驱动通过 compatible 属性匹配设备。

&i2c1 {
    status = "okay";
    sensor@48 {
        compatible = "vendor,temp-sensor";
        reg = <0x48>;
    };
};

升级内核时,必须同步检查:驱动是否已合入、设备树是否更新、模块版本是否匹配、固件文件是否随根文件系统一起更新。只换内核不换设备树,往往导致外设失效。

五、常见发行版与构建系统

嵌入式 Linux 很少直接使用桌面发行版,而是通过构建系统生成定制镜像。常见方案如下表:

构建系统 特点 适用场景
Buildroot 轻量、Makefile 驱动、生成完整镜像 中小型产品、快速定制
Yocto / OpenEmbedded 分层配方、可复现构建、SDK 完善 大型项目、长期维护
OpenWrt 网络设备导向、包管理、overlay 路由器、网关
Debian / Ubuntu 根文件系统 包管理成熟、体积较大 算力较强、需要 apt 维护

选择构建系统直接影响升级方式:Buildroot 适合整镜像 A/B 升级;Yocto 可做包级增量升级;OpenWrt 自带 sysupgrade 机制。维护方案必须与构建系统匹配。

核心结论:嵌入式 Linux 升级的本质,是对“引导链 + 内核 + 设备树 + 根文件系统 + 应用”这一整条启动链的版本管理。理解启动顺序,才能定位失败环节;理解根文件系统布局,才能设计回滚;理解内核与驱动关系,才能避免“换了内核外设不工作”。

六、场景与误区复盘

某网关产品升级后无法启动,串口停在 U-Boot,提示 Bad Magic Number。排查发现升级包中的内核镜像未加 U-Boot 头,而 bootcmd 使用 bootm 加载。改为 bootz 或重新制作 uImage 后恢复。这说明升级包格式必须与引导方式严格匹配。

另一个常见误区是:只备份根文件系统,不备份 Bootloader 环境变量。一旦 bootargs 被改错,系统同样无法启动。建议在升级前导出 printenv,并保留串口恢复通道。

维护建议:为每个版本建立“版本清单”,记录 Bootloader、内核、设备树、根文件系统、应用、配置的版本号与校验值。升级前校验,升级后核对,回滚时按清单恢复。

嵌入式 Linux 系统组成与启动链 系统组成(静态视图) 应用与服务层 业务程序 / 升级代理 / 中间件 根文件系统层 /bin /lib /etc /dev /data 内核层 调度 / 内存 / 驱动 / 设备树 引导层 BootROM / SPL / U-Boot 启动 启动流程(动态视图) ① 上电复位 → BootROM 选择启动介质 ② SPL 初始化 DDR → 加载 U-Boot ③ U-Boot 加载 kernel + dtb + rootfs ④ Kernel 初始化 → 挂载根文件系统 ⑤ 执行 init → 启动用户态服务 升级维护必须维护的“版本契约” Bootloader ↔ 内核镜像格式 | 内核 ↔ 驱动模块 | 内核 ↔ 设备树 | RootFS ↔ 工具链 ABI | 应用 ↔ 动态库 任一契约断裂,都会导致启动失败、外设失效或业务异常

把这张图记在脑子里,后续设计升级方案时就能快速判断:这次升级动了哪一层、影响哪些契约、需要哪些校验与回滚手段。基础越清晰,维护方案越可靠。