3. 嵌入式Linux系统启动流程
嵌入式Linux的启动不是“上电即进Shell”,而是一条严格分阶段的接力链:芯片内部固件先把最简存储介质中的Bootloader搬进内存,Bootloader再准备内核运行环境并传递参数,内核完成自解压、早期初始化、驱动探测与挂载根文件系统,最后由用户空间的init进程启动服务并进入Busybox提供的Shell或应用。理解这条链路,是后续做Busybox驱动集成与板卡适配的前提——因为任何一环的参数、路径或设备节点不匹配,系统都可能停在“Kernel panic”或“No init found”。
一、启动链的五个关键阶段
典型ARM/ARM64嵌入式板卡的启动链可拆为:ROM Code → Bootloader(SPL/TPL + U-Boot)→ Linux Kernel → Device Tree → Rootfs + Busybox init。它们不是并列关系,而是前后依赖的时序关系。
- ROM Code:芯片出厂固化,按拨码或eFuse顺序扫描SD、eMMC、SPI Flash、USB等启动介质,加载第一级引导头。
- Bootloader:通常分两级。SPL负责初始化DDR并加载完整U-Boot;U-Boot负责网络、存储、环境变量、加载内核与设备树,并通过bootargs传递启动参数。
- Kernel:自解压后进入start_kernel,完成内存管理、调度、中断、时钟初始化,再根据设备树匹配并probe驱动。
- Device Tree:以.dts/.dtb描述板级硬件拓扑,内核据此知道有哪些UART、I2C、GPIO、MMC控制器及其地址、中断、时钟。
- Rootfs与Busybox:内核挂载根文件系统后执行/init或/sbin/init,Busybox以单一可执行文件提供sh、ls、mount、ifconfig等命令,并常作为init程序完成服务启动。
二、Bootloader与内核的交接:参数比代码更易出错
U-Boot阶段最关键的产物是内核镜像、设备树二进制和bootargs。常见加载命令如下:
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait init=/sbin/init'
fatload mmc 0:1 0x80008000 zImage
fatload mmc 0:1 0x83000000 imx6ull-board.dtb
bootz 0x80008000 - 0x83000000
其中root=决定根文件系统位置,rootwait让内核等待MMC枚举完成,init=指定第一个用户空间进程。若把root=/dev/mmcblk0p2误写成/dev/mmcblk1p2,内核会报“VFS: Cannot open root device”,此时Busybox根本没有机会运行。
三、设备树:内核与板卡之间的“硬件合同”
设备树把板级差异从内核源码中剥离。以UART为例,.dts中描述寄存器地址、中断号、时钟和引脚复用;内核启动时由of_platform_populate扫描节点,匹配compatible字符串后调用对应驱动probe。若设备树中缺少某个节点,驱动不会报“找不到硬件”,而是根本不创建platform_device,表现为“设备不存在”。
&uart1 {
compatible = "fsl,imx6ul-uart", "fsl,imx6q-uart";
reg = <0x02020000 0x4000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_UART1_IPG>,
<&clks IMX6UL_CLK_UART1_SERIAL>;
status = "okay";
};
四、根文件系统与Busybox init:启动链的最后一公里
内核挂载根文件系统后,会依次尝试/sbin/init、/etc/init、/bin/init、/bin/sh。在Busybox方案中,通常把/sbin/init软链接到/bin/busybox,由Busybox根据argv[0]判断进入init模式,读取/etc/inittab:
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty -L ttyS0 115200 vt100
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
rcS脚本中常通过mdev -s生成/dev节点,通过insmod加载板卡驱动模块。若根文件系统中缺少/dev/console,内核会提示“Warning: unable to open an initial console”,虽然系统可能继续启动,但串口交互会异常。
五、启动链关键对象与排障对照
| 阶段 | 关键对象 | 典型失败现象 | 排查入口 |
|---|---|---|---|
| ROM Code | 启动介质、拨码 | 串口无任何输出 | 确认SD/eMMC启动模式与镜像头 |
| Bootloader | SPL、U-Boot、bootargs | 停在U-Boot命令行或DDR初始化失败 | 检查DDR配置、环境变量、加载地址 |
| Kernel | zImage、驱动probe | Kernel panic、驱动未加载 | 看dmesg、compatible匹配、时钟/中断 |
| Device Tree | .dtb、节点status | 设备不存在、引脚无功能 | 反编译dtb核对节点与pinctrl |
| Rootfs/Busybox | /init、inittab、rcS | No init found、无法进入Shell | 检查root=、init=、/dev/console、权限 |
综上,Bootloader负责“把内核和板卡描述送进内存”,内核负责“按设备树初始化硬件并挂载根文件系统”,Busybox则在用户空间完成“从init到Shell”的最后一公里。后续做Busybox驱动集成时,驱动模块的加载时机、设备节点生成和启动脚本顺序,都必须放回这条启动链中审视,才能保证板卡从上电到业务进程稳定运行。