2. 嵌入式Linux启动流程

嵌入式Linux的启动过程,本质上是一条从“裸机硬件”到“可用用户空间”的接力链。很多初学者把启动失败笼统归因于“系统没起来”,但在工程实践中,必须精确判断问题发生在哪一棒:是Bootloader没有正确加载内核,还是内核找不到根文件系统,抑或是init进程没有拉起关键服务。理解Bootloader、内核、RootFS三者的职责边界,以及init进程如何衔接内核态与用户态,是后续裁剪BusyBox、定制RootFS的前提。

一、三阶段职责划分:谁负责什么

嵌入式Linux启动通常划分为三个阶段,每个阶段有明确的输入、输出和失败特征。

  • Bootloader阶段:上电后最先运行,负责初始化DDR、时钟、串口等最基础硬件,然后从Flash、SD卡、网络或UART加载内核镜像(zImage/Image)和设备树(.dtb),最后跳转到内核入口。它不关心文件系统里有什么应用。
  • 内核阶段:解压自身、初始化内存管理、调度、驱动模型,挂载根文件系统。内核只负责“让系统具备运行用户程序的能力”,不负责决定要运行哪些业务服务。
  • RootFS与用户空间阶段:提供/bin、/sbin、/etc、/lib等目录和可执行程序,由内核启动的第一个用户态进程init,按配置拉起服务、挂载、网络、登录等,最终形成可用系统。

可以用一句话概括:Bootloader把内核“搬进来”,内核把RootFS“挂起来”,init把用户空间“跑起来”。

嵌入式Linux启动接力链与职责边界 ① Bootloader 初始化DDR/时钟/串口 加载 zImage + .dtb 跳转内核入口 失败特征:无串口输出/卡在U-Boot ② Linux内核 解压、初始化内存/调度 匹配设备树、加载驱动 挂载根文件系统 失败特征:Kernel panic ③ RootFS + init /sbin/init 启动 解析 inittab / 服务脚本 拉起应用、登录、网络 失败特征:Kernel panic - not syncing: No init found 用户空间启动链路(以 BusyBox init 为例) 内核启动 /sbin/init 读取 /etc/inittab sysinit / respawn 执行 /etc/init.d/rcS 挂载、配置、启动 启动业务进程 app / sshd / telnetd 进入登录/控制台 系统可用

二、init进程:内核态与用户态的交接点

内核在完成初始化后,会尝试执行第一个用户态程序。这个程序由内核命令行参数init=指定,若未指定则按顺序查找/sbin/init、/etc/init、/bin/init、/bin/sh。在嵌入式系统中,这个程序通常就是BusyBox提供的init。

BusyBox init的行为由/etc/inittab驱动,其核心动作类型包括:

  • sysinit:系统初始化阶段执行一次,通常指向/etc/init.d/rcS。
  • respawn:进程退出后自动重启,适合常驻服务,如ttyS0::respawn:/sbin/getty。
  • askfirst:等待用户按键后再启动,常用于调试串口登录。
  • once:只执行一次,不重启。
  • shutdown:系统关机时执行。

一个典型的/etc/inittab片段如下:

# /etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty -L ttyS0 115200 vt100
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r

而/etc/init.d/rcS则负责挂载伪文件系统、配置网络、启动应用:

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp
ifconfig lo 127.0.0.1 up
ifconfig eth0 192.168.1.10 up
/usr/sbin/telnetd -l /bin/sh &
/opt/app/myapp &

三、启动阶段对照表:现象、原因与定位手段

阶段 典型输出/现象 常见原因 定位手段
Bootloader 串口无输出,或停在U-Boot提示符 DDR参数错误、启动介质选择错误、bootcmd未配置 检查printenv、bootargs、bootcmd
内核加载 Starting kernel ... 后无输出 内核镜像地址错误、设备树不匹配、串口驱动未使能 核对bootm地址、console=参数
根文件系统挂载 Kernel panic - not syncing: VFS: Unable to mount root fs root=参数错误、存储驱动缺失、文件系统类型不匹配 检查root=/dev/mmcblk0p2、rootfstype=ext4
init启动 Kernel panic - not syncing: No init found /sbin/init缺失、权限错误、动态库缺失 用init=/bin/sh进入应急shell排查
用户空间服务 系统能登录但业务未启动 rcS未执行、脚本权限不足、依赖库缺失 查看ps、dmesg、手动执行脚本

四、实战场景:从“卡在No init found”到定位RootFS问题

某项目使用BusyBox构建RootFS,内核启动后报错:

[    2.134567] Kernel panic - not syncing: No init found.
Try passing init= option to kernel.

排查思路如下:

  1. 确认内核命令行:cat /proc/cmdline显示root=/dev/mmcblk0p2 rootfstype=ext4,参数正确。
  2. 在U-Boot中临时修改启动参数为init=/bin/sh,重新启动后成功进入shell,说明内核和根文件系统挂载均正常。
  3. 在应急shell中执行ls -l /sbin/init,发现该文件不存在,只有/bin/busybox。
  4. 检查RootFS制作脚本,发现创建/sbin/init软链接的步骤被遗漏。
  5. 补上ln -s /bin/busybox /sbin/init后重新打包镜像,系统正常启动。
应急技巧:当init启动失败时,在内核命令行追加init=/bin/sh或rdinit=/bin/sh,可以绕过init直接进入shell,快速判断问题出在RootFS内容还是内核配置。

五、常见误区与复盘要点

  • 误区一:把“内核启动”等同于“系统启动”。内核打印出Freeing unused kernel memory只代表内核初始化完成,真正决定系统可用性的是init和用户空间服务。
  • 误区二:忽略动态库依赖。BusyBox若编译为动态链接,RootFS中必须包含/lib/ld-linux.so.3等加载器和依赖库,否则init无法执行。
  • 误区三:inittab与rcS职责混淆。inittab负责“启动哪些进程”,rcS负责“启动前做什么配置”,两者应分层清晰,避免把所有逻辑堆在inittab中。
  • 复盘要点:每次启动失败,先看串口最后一行输出,再判断属于哪个阶段,最后用最小化手段(如init=/bin/sh)缩小范围,而不是盲目重新烧写整个镜像。

掌握这条启动链路后,后续裁剪BusyBox、精简RootFS、替换init方案时,就能清楚知道每一步改动会影响哪个阶段,从而做到有的放矢。