2. 嵌入式Linux启动流程

嵌入式 Linux 的启动并不是“上电即进入系统”,而是一条严格分阶段的接力链:Bootloader 先把最基础的硬件唤醒并搬运内核,内核完成子系统初始化后再去寻找并挂载根文件系统,最后由 Rootfs 中的 init 进程把用户空间拉起来。理解这条链路,尤其是 Rootfs 的挂载时机,是后续自己构建根文件系统时判断“为什么卡住”“为什么进不了 shell”的关键。

一、核心原理:三段式接力与 Rootfs 的角色

从职责上看,启动过程可以抽象为三个角色:

  • Bootloader(如 U-Boot):负责上电后的最小初始化(时钟、DDR、串口),从 Flash/eMMC/SD/TFTP 读取内核镜像与设备树,设置 bootargs,跳转到内核入口。
  • Kernel:解压自身、初始化内存管理、调度、驱动模型,挂载根文件系统,并执行其中的 /sbin/init(或 init= 指定的程序)。
  • Rootfs:提供用户空间所需的目录结构、动态库、init 程序、配置与服务脚本。没有可挂载的 Rootfs,内核在 VFS: Cannot open root device 处就会 panic。

因此 Rootfs 在启动中的角色可以概括为:内核与用户空间之间的“交接平台”。内核只负责把 Rootfs 挂到 /,真正让系统“活起来”的是 Rootfs 里的 init 及其启动脚本。

关键结论:Bootloader 决定“内核从哪里来”,内核决定“Rootfs 从哪里来、怎么挂”,Rootfs 决定“系统起来后长什么样”。

二、方法流程:从复位向量到用户空间

以常见的 ARM + U-Boot + Linux 为例,启动顺序可细化为:

  1. 上电复位,CPU 从 BootROM 执行,加载 SPL/MLO 到片内 SRAM。
  2. SPL 初始化 DDR,加载完整 U-Boot 到内存并跳转。
  3. U-Boot 执行 bootcmd,加载 zImage/Image 与 .dtb,传递 bootargs。
  4. 内核解压、初始化,解析设备树,注册平台设备与驱动。
  5. 内核根据 root= 与 rootfstype= 挂载根文件系统。
  6. 执行 /sbin/init,进入用户空间,启动服务与登录界面。

其中第 5 步就是 Rootfs 的挂载时机。它发生在内核驱动初始化基本完成之后、init 启动之前。挂载方式常见有三类:

方式典型 bootargs适用场景
块设备根文件系统root=/dev/mmcblk0p2 rootfstype=ext4 rweMMC/SD 卡上的 ext4/UBIFS
Initramfs/Initrdroot=/dev/ram0 rdinit=/init先挂临时根,再切换真实根
NFS 网络根root=/dev/nfs nfsroot=192.168.1.10:/nfsroot ip=dhcp开发调试阶段

一个典型的 U-Boot 环境变量与内核日志片段如下:

setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait'
setenv bootcmd 'mmc dev 0; fatload mmc 0:1 0x80008000 zImage; fatload mmc 0:1 0x83000000 sun8i.dtb; bootz 0x80008000 - 0x83000000'
saveenv

# 内核启动日志
[    0.000000] Kernel command line: console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait
[    1.234567] mmc0: new high speed SDHC card
[    1.456789] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode
[    1.460000] VFS: Mounted root (ext4 filesystem) on device 179:2.
[    1.470000] Run /sbin/init as init process

三、案例/场景:Rootfs 挂载失败的典型排查

假设板子上电后串口停在如下位置:

[    1.460000] VFS: Cannot open root device "mmcblk0p2" or unknown-block(179,2)
[    1.460000] Please append a correct "root=" boot option
[    1.460000] Kernel panic - not syncing: VFS: Unable to mount root fs

这说明内核已经跑起来了,但找不到 Rootfs。排查顺序建议如下:

  • 确认分区是否存在:mmcblk0p2 是否被识别,分区表是否正确。
  • 确认文件系统类型:rootfstype=ext4 是否与实际格式一致。
  • 确认驱动是否编入内核:MMC 控制器驱动、EXT4 驱动是否已编译进内核而非模块。
  • 确认 rootwait:对于 SD/eMMC,建议加上 rootwait,等待设备就绪。
调试技巧:在 U-Boot 中临时修改 bootargs 并 boot,可以快速验证不同 root 参数,而不必重新烧写整个系统。

四、误区与复盘:Rootfs 不是“最后才考虑”的部分

很多初学者把 Rootfs 当成“系统跑起来之后再放进去的东西”,这是最大的误区。实际上,内核在启动后期会主动寻找 Rootfs,如果找不到就直接 panic。因此 Rootfs 的存在性、格式、驱动支持必须在设计启动方案时就确定。

另一个常见误区是混淆 initramfs 与真实根文件系统。initramfs 是内核内置的临时根,用于加载必要驱动后再切换根;而真实 Rootfs 才是最终用户空间。两者挂载时机不同,职责也不同。

复盘时可以记住这条主线:Bootloader 搬内核 → 内核挂 Rootfs → Rootfs 启 init。任何一环断裂,系统都无法进入用户空间。下一节我们将从目录结构开始,真正动手规划一个可启动的 Rootfs。

嵌入式 Linux 启动流程与 Rootfs 挂载时机 ① Bootloader U-Boot / SPL 初始化 DDR / 串口 加载 zImage + dtb 设置 bootargs 跳转 ② Kernel 解压 / 初始化子系统 解析设备树 注册驱动 / 探测设备 准备挂载 Rootfs 挂载 ③ Rootfs 挂载到 / 提供 /sbin/init 启动用户空间服务 进入 shell / 应用 t0: 上电复位 t1: 内核启动 t2: Rootfs 挂载 Rootfs 挂载时机与关键参数 块设备根 root=/dev/mmcblk0p2 Initramfs rdinit=/init NFS 根 nfsroot=192.168.1.10:/nfsroot 挂载失败 → VFS: Cannot open root device → Kernel panic 挂载成功 → Run /sbin/init as init process → 用户空间启动