2. 嵌入式 Linux 启动流程
在嵌入式 Linux 系统中,从上电到出现可交互的 shell,并不是某一个程序独立完成的任务,而是 Bootloader、Kernel、Rootfs 三个角色按严格顺序接力协作的结果。理解这条链路,是后续用 BusyBox 定制根文件系统、决定“根文件系统放在哪里、怎么被挂载”的前提。很多启动失败问题,本质上不是内核坏了,而是三者之间的“交接约定”没有对齐。
一、核心原理:三阶段接力与职责边界
可以把整个启动过程理解为一次接力赛,每一棒只负责把系统推进到下一棒能够接手的程度:
- Bootloader 阶段:芯片上电后先执行固化在 ROM 中的一级引导,再跳转到 Bootloader(如 U-Boot)。它负责最基础的时钟、DDR、串口初始化,然后从存储介质中把内核镜像和设备树加载到内存,最后把控制权交给内核。
- Kernel 阶段:内核解压并自解压后开始运行,完成内存管理、中断、驱动模型、调度器等核心子系统的初始化,并根据
bootargs中的root=参数去定位根文件系统。 - Rootfs 阶段:内核挂载根文件系统后,执行其中的
/sbin/init(或/init),由 init 进程拉起服务、挂载其余文件系统,最终启动登录 shell。对 BusyBox 系统而言,这个 init 通常就是 BusyBox 提供的init或busybox init。
三者的关键交接点有两个:一是 Bootloader 向内核传递 启动参数(bootargs)和 机器/设备树信息;二是内核向用户空间传递 根文件系统位置。任何一处参数写错,都会表现为“内核起来了但找不到根文件系统”或“挂载成功但没有 init”。
二、方法流程:从 bootargs 到 init 的完整链路
以 U-Boot + 内核 + BusyBox rootfs 的典型组合为例,启动参数通常这样设置:
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait init=/sbin/init'
setenv bootcmd 'mmc dev 0; fatload mmc 0:1 0x80008000 zImage; fatload mmc 0:1 0x83000000 imx6ull.dtb; bootz 0x80008000 - 0x83000000'
saveenv
boot
内核启动后,会依次完成以下动作:
- 解析
root=指定的设备节点,等待该块设备就绪(rootwait常用于 eMMC/SD 卡)。 - 按
rootfstype=指定的文件系统类型挂载根分区。 - 若挂载成功,执行
init=指定的程序;未指定时按/sbin/init、/etc/init、/bin/init、/bin/sh顺序查找。 - BusyBox init 读取
/etc/inittab,按sysinit、respawn、askfirst等动作启动控制台和脚本。
如果使用 Initramfs,则内核在挂载真正的根文件系统之前,会先把内嵌的 cpio 归档解压到内存中的 rootfs,执行其中的 /init,由它完成驱动加载、分区检查后再 switch_root 到真实根分区。这也是很多量产设备“先内存启动、再切换到 eMMC”的常见做法。
三、案例场景:三种典型加载方式对比
| 加载方式 | 典型 bootargs | 适用场景 | 注意点 |
|---|---|---|---|
| 块设备根文件系统 | root=/dev/mmcblk0p2 rootfstype=ext4 |
量产设备、eMMC/SD 卡 | 分区号、文件系统类型必须与烧录一致 |
| NFS 网络根文件系统 | root=/dev/nfs nfsroot=192.168.1.10:/nfsroot |
开发调试、快速迭代 | 需内核支持 NFS,网络与 IP 参数要正确 |
| Initramfs / RAMDisk | root=/dev/ram0 或内嵌 cpio |
救援系统、两阶段启动 | 占用内存,需处理 switch_root |
四、误区与复盘:启动失败的高频原因
误区一:内核启动成功就等于系统启动成功。 内核打印出 VFS: Cannot open root device 或 Kernel panic - not syncing: No init found 时,问题往往在 rootfs 侧,而不是内核本身。
- root= 设备名不匹配:eMMC 在不同内核版本下可能是
/dev/mmcblk0p2或/dev/mmcblk1p2,建议用root=PARTUUID=或root=UUID=提高稳定性。 - 缺少 init 或权限错误:BusyBox 生成的
/sbin/init必须是可执行文件,且动态链接时/lib下的库要齐全,否则会报No init found。 - inittab 配置错误:BusyBox init 依赖
/etc/inittab,若该文件缺失或格式错误,控制台可能无法拉起 shell。 - 文件系统类型不匹配:把 ext4 分区写成
rootfstype=jffs2,内核会直接挂载失败。
复盘建议: 排查启动问题时,按“Bootloader 是否加载成功 → 内核是否解压运行 → root= 是否被正确解析 → 根文件系统是否挂载 → init 是否执行”的顺序逐段确认。串口日志中的每一行都是定位接力断点的关键证据,尤其是 Kernel command line 这一行,它直接反映了实际生效的 bootargs。
理解了 Bootloader、Kernel、Rootfs 的协作关系与加载方式,接下来用 BusyBox 构建根文件系统时,就能明确“目录结构要满足 init 查找规则、库文件要满足动态链接需求、inittab 要满足 BusyBox init 的解析格式”,从而让定制出的 rootfs 真正能被内核顺利接管。