kernel boot 参数 init 怎么传递 BusyBox init 或直接 shell
什么是内核启动参数
Linux 内核的 boot command line 是一段字符串,由 bootloader(GRUB、U-Boot、UEFI)在启动时传给内核。内核解析其中的各种 key=value 参数来决定行为——包括用什么方式挂载根文件系统、用什么程序作为第一个用户态进程(PID 1)。
关键参数:
| 参数 | 说明 |
|---|---|
init=/path/to/program | 指定 PID 1 执行的程序路径 |
initrd=/path/to/archive | 指定 initramfs 归档文件位置 |
root=/dev/xxx | 指定根分区设备号 |
rootfstype=ext4 | 指定根文件系统类型 |
ro / rw | 根文件系统初始只读/读写 |
console=ttyS0,115200 | 内核消息输出到哪里 |
panic=N | kernel panic 后 N 秒自动重启(0=死等) |
init= 参数的三种用法
方式一:跳过 init 直接进 shell(调试用)
init=/bin/sh
内核启动后直接以 /bin/sh(BusyBox ash 或 hush)作为 PID 1。没有 inittab、没有 rcS,拿到 shell 后需要手动挂载 proc/sys/dev:
# shell 启动后手动初始化
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mount -o remount,rw /
这个方式在系统出问题(inittab 写错、rcS 挂掉)时用来救援特别方便——绕过了正常的 init 流程直接进 shell 修。
方式二:指定 BusyBox 二进制(推荐)
init=/busybox init
或者更简单(因为 BusyBox 支持 argv[0] 分发):
init=/init
内核启动时把 /init 作为 PID 1。如果 /init 是指向 busybox 的符号链接(/init -> busybox),那么 BusyBox 会被调用成 "init",执行 BusyBox init 逻辑——读取 /etc/inittab,执行 rcS,respawn 登录 shell 等。
make install 默认就会在 CONFIG_PREFIX/ 下创建 sbin/init -> busybox。
方式三:启动你的自定义程序
init=/usr/local/bin/my-daemon
内核启动后直接运行你的程序作为 PID 1,完全跳过 BusyBox init 或 shell。这在只运行单一应用的嵌入式设备上有用——比如工业控制器只跑一个主控程序。你的程序需要自己处理信号(SIGTERM 时优雅退出),并决定是否启动其他进程。
initrd vs initramfs
| 方式 | 格式 | 说明 |
|---|---|---|
| initrd | cpio.gz | 传统方式,内核解压后放到临时文件系统 |
| initramfs | cpio.gz | 现代方式,解压到 rootfs,与根文件系统合并 |
两者对用户态代码完全透明——你只要把 BusyBox 和根文件系统结构打包成 cpio.gz,内核会自动处理。initrd= 和 initramfs= 参数最终效果是一样的(现代内核都用 initramfs)。
典型 bootloader 配置
U-Boot(嵌入式 ARM 设备最常见的 bootloader):
# 设置启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 init=/init'
# 加载内核
fatload mmc 0:1 0x20800000 uImage
# 加载 initramfs(如果用)
fatload mmc 0:1 0x21800000 initramfs.cpio.gz
# 启动
bootm 0x20800000 0x21800000
GRUB(x86 服务器/虚拟机):
menuentry "BusyBox system" {
linux (hd0,1)/boot/bzImage root=/dev/sda1 ro init=/init
initrd (hd0,1)/boot/initramfs.cpio.gz
}
QEMU(开发测试):
qemu-system-x86_64 \
-kernel bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 init=/init" \
-nographic
调试内核启动参数
如果系统启动失败,最简单的调试方法是给 bootargs 加 init=/bin/sh,绕过所有 init 流程直接进 shell,然后逐步排查:
# 拿到 shell 后
ls -la /etc/inittab # 文件在不在?
cat /etc/inittab # 格式对不对?
busybox init # 手动触发 init,看报错
加 panic=1 也有用——kernel panic 后 1 秒自动重启(而不是卡在那里),配合串口控制台可以快速看到 panic 信息。