2. 嵌入式Linux基础

嵌入式 Linux 不是“把桌面 Linux 缩小”,而是一套围绕特定硬件、特定用途重新裁剪和组织的软件栈。理解它的系统组成、启动链路、交叉编译方式、根文件系统形态以及发行版选型逻辑,是后续做量产部署、固件升级和现场排障的基础。本节从工程视角梳理这五个关键点,并给出可落地的判断依据。

一、系统组成:四层结构,各司其职

一个典型的嵌入式 Linux 系统可以拆成四层:引导层、内核层、根文件系统层和应用层。引导层负责把处理器从复位状态带到能加载内核的状态,常见组件包括 BootROM、SPL、U-Boot、TF-A 等;内核层负责进程调度、内存管理、驱动框架和外设支持;根文件系统层提供用户空间运行所需的最小目录、库、配置和工具;应用层则是业务逻辑、通信协议、UI 和升级代理等。

这四层之间存在明确的依赖关系:引导层决定内核能否被正确加载和传参,内核决定根文件系统能否被挂载,根文件系统决定应用能否启动。量产中最常见的“起不来”问题,往往不是应用代码错误,而是这四层中某一层的接口或参数不匹配。

二、启动流程:从上电到第一个用户进程

嵌入式 Linux 的启动流程可以概括为一条链:上电复位 → BootROM → 一级引导 → 二级引导 → 内核解压与初始化 → 挂载根文件系统 → init 进程 → 应用启动。不同 SoC 的细节不同,但主干一致。

以常见的 ARM + U-Boot 方案为例,启动阶段的关键动作如下:

BootROM: 从 eMMC/SD/SPI 读取 SPL
SPL:    初始化 DDR、时钟,加载 U-Boot proper
U-Boot: 加载 kernel、dtb、ramdisk,设置 bootargs
Kernel: 解压、初始化驱动、挂载 rootfs
init:   执行 /sbin/init 或 /init,启动服务

其中 bootargs 是启动链中最容易出错的参数之一,它决定了内核如何找到根文件系统。例如:

setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw'

如果 root= 指向错误分区,或者 rootwait 缺失导致内核在存储设备就绪前尝试挂载,系统就会停在 VFS: Cannot open root device 一类错误上。因此,启动流程的调试重点通常集中在串口日志、分区表和 bootargs 三处。

嵌入式 Linux 启动链路与四层系统组成 BootROM 上电复位 / 启动介质选择 SPL / TF-A DDR / 时钟初始化 U-Boot 加载 kernel / dtb / bootargs Kernel 驱动 / 挂载 rootfs init 应用启动 四层系统组成与关键交付物 引导层 BootROM / SPL U-Boot / TF-A bootargs / 分区表 内核层 zImage / Image dtb / 驱动模块 内核配置与裁剪 根文件系统层 BusyBox / systemd glibc / musl / uClibc /etc /lib /bin /dev 应用层 业务进程 通信 / UI 升级代理

三、交叉编译:在 x86 上生成 ARM 可执行文件

嵌入式设备通常没有足够的算力和存储完成本地编译,因此采用交叉编译:在性能更强的开发主机(通常是 x86_64 Linux)上,使用目标架构的工具链生成可在目标板运行的二进制。交叉编译的核心是“三件套”:工具链前缀、sysroot和目标架构参数。

以 ARM 32 位为例,常见工具链前缀为 arm-linux-gnueabihf-,编译命令如下:

arm-linux-gnueabihf-gcc -o hello hello.c \
  --sysroot=/opt/toolchain/arm/sysroot \
  -march=armv7-a -mfpu=neon -mfloat-abi=hard

交叉编译最容易踩的坑是“主机库混入”:如果 sysroot 设置不完整,链接器可能找到主机上的 libc.so,导致目标板运行时报 GLIBC_2.xx not found。另一个常见问题是浮点 ABI 不一致,例如目标板是硬浮点,而工具链按软浮点编译,表现为程序启动即崩溃。工程上建议把工具链、sysroot、内核头文件和根文件系统库版本统一纳入版本管理,并在 CI 中固定。

四、根文件系统:最小可运行集合与形态选择

根文件系统(rootfs)是内核挂载后用户空间看到的完整目录树。它至少需要包含:/bin、/sbin、/lib、/etc、/dev、/proc、/sys、/tmp 以及 init 程序。量产中常见的 rootfs 形态有四种:

形态 特点 适用场景 注意事项
initramfs 随内核打包,内存中解压 极小系统、恢复模式 占用 RAM,不适合大 rootfs
只读 squashfs 压缩只读,配合 overlay 量产固件、防篡改 需额外可写分区保存配置
ext4 可读写 通用、易调试 开发阶段、工控机 异常断电可能损坏文件系统
UBIFS / JFFS2 面向 MTD / NAND NOR/NAND 存储设备 需匹配 MTD 分区与擦除块

选择 rootfs 形态时,核心判断是:是否需要现场写入、存储介质是什么、断电可靠性要求多高。量产设备通常采用“只读根 + 可写数据分区 + overlay”的组合,以兼顾稳定性和可维护性。

五、常见发行版选型:Buildroot、Yocto、Debian 与 OpenWrt

嵌入式 Linux 发行版不是只有一种答案。Buildroot 适合“小而快”的定制系统,配置简单、构建产物精简;Yocto 适合“大而稳”的产品线,层机制和配方体系便于多硬件、多版本复用,但学习曲线陡峭;Debian/Ubuntu 适合算力充足、需要丰富软件包的设备,但镜像体积和启动时间较大;OpenWrt 则专注网络设备,拥有成熟的包管理和 Web 配置框架。

选型建议:如果产品只需要固定功能、追求启动速度和镜像体积,优先 Buildroot;如果产品线长、需要长期维护和 BSP 复用,优先 Yocto;如果设备算力充足且需要快速集成大量开源软件,可考虑 Debian;如果是路由器、网关类设备,OpenWrt 往往是最省力的起点。

六、误区与复盘:三个高频问题

  • 把开发板镜像直接当量产镜像。开发板镜像通常包含调试工具、默认密码和可写根分区,量产前必须裁剪、加固并关闭调试接口。
  • 忽略 bootargs 与分区表的耦合。分区号、文件系统类型、rootwait 参数必须与量产分区方案一致,否则升级后容易无法启动。
  • 交叉编译工具链与 rootfs 库版本不一致。这类问题在开发阶段可能不暴露,量产后才表现为随机崩溃或符号缺失,必须在构建阶段锁定版本。

建议在项目早期就建立“启动链路检查清单”:BootROM 启动介质、SPL 加载地址、U-Boot 环境变量、内核镜像格式、dtb 匹配、bootargs、rootfs 分区与文件系统类型。每次硬件或分区变更后,按清单逐项验证,可以显著降低量产阶段的启动故障率。

掌握系统组成、启动流程、交叉编译、根文件系统和发行版选型这五个基础点后,后续的驱动移植、固件升级、安全加固和产线烧录才有稳定的落脚点。它们共同决定了嵌入式 Linux 产品能否从“能跑”走向“可量产、可维护、可升级”。