BusyBox 搭建 mini rootfs 完全指南 / 第 5 章:Init 系统详解
第 5 章:Init 系统详解
5.1 Linux Init 系统概述
5.1.1 Init 进程的角色
Init 是 Linux 系统中第一个用户空间进程(PID=1),由内核直接启动。它负责:
- 初始化系统环境
- 挂载文件系统
- 启动系统服务
- 管理运行级别
- 处理系统关机/重启
# 查看 PID 1
$ ps -p 1 -o pid,comm
PID COMMAND
1 init
# 或使用 BusyBox ps
$ busybox ps -p 1
PID USER COMMAND
1 root init
5.1.2 常见 Init 系统对比
| Init 系统 | 复杂度 | 特点 | 典型应用 |
|---|
| BusyBox init | 低 | 轻量、适合嵌入式 | OpenWrt、嵌入式设备 |
| SysVinit | 中 | 经典,基于脚本 | 传统 Linux 发行版 |
| systemd | 高 | 功能强大、并行启动 | Ubuntu、RHEL、Debian |
| OpenRC | 中 | 轻量、兼容 SysV | Gentoo、Alpine(旧版) |
| runit | 低 | 极简、服务监督 | Void Linux |
| s6 | 低 | 高可靠性 | 工业嵌入式 |
5.1.3 为什么选择 BusyBox init
| 优势 | 说明 |
|---|
| 体积小 | 集成在 BusyBox 中,无额外开销 |
| 简单 | 配置文件 inittab 易于理解 |
| 依赖少 | 不需要 D-Bus、systemd 等 |
| 足够 | 满足嵌入式和容器的基本需求 |
| 灵活 | 可通过脚本扩展功能 |
5.2 inittab 配置文件
5.2.1 文件位置和格式
# 默认路径
/etc/inittab
# 格式
<id>:<runlevels>:<action>:<process>
5.2.2 字段详解
| 字段 | 说明 | 示例 |
|---|
id | 控制终端标识 | tty1, console, ttyS0 |
runlevels | 运行级别(BusyBox 忽略此字段) | 留空或填 12345 |
action | 动作类型 | sysinit, respawn, askfirst |
process | 要执行的命令 | /bin/sh, /sbin/getty |
5.2.3 action 类型说明
| action | 说明 |
|---|
sysinit | 系统初始化时执行(最先执行) |
respawn | 进程退出后自动重启 |
askfirst | 类似 respawn,但先提示用户按回车 |
wait | 等待进程执行完毕再继续 |
once | 执行一次,不等待完成 |
ctrlaltdel | 用户按下 Ctrl+Alt+Del 时执行 |
shutdown | 系统关机时执行 |
restart | init 收到 SIGHUP 信号时执行 |
5.2.4 完整 inittab 示例
# /etc/inittab - BusyBox init 完整配置
# ============================================================
# 系统初始化阶段
# ============================================================
# 挂载虚拟文件系统
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -t sysfs sysfs /sys
::sysinit:/bin/mount -t tmpfs tmpfs /tmp
::sysinit:/bin/mount -t devtmpfs devtmpfs /dev
# 运行初始化脚本
::sysinit:/etc/init.d/rcS
# ============================================================
# 终端定义
# ============================================================
# 串口控制台(嵌入式设备常用)
ttyS0::respawn:-/bin/sh
# 虚拟控制台(桌面或带显示器设备)
tty1::respawn:-/bin/sh
tty2::respawn:-/bin/sh
tty3::respawn:-/bin/sh
# 串口登录(带 getty)
# ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
# ============================================================
# 特殊功能
# ============================================================
# Ctrl+Alt+Del 重启
::ctrlaltdel:/sbin/reboot
# 关机时执行
::shutdown:/bin/umount -a -r
::shutdown:/bin/sync
# 重启时执行
::restart:/sbin/init
# ============================================================
# 可选:嵌入式应用
# ============================================================
# 启动自定义应用
# ::once:/usr/bin/myapp
5.2.5 最简 inittab
# 仅启动 shell(开发调试用)
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -t sysfs sysfs /sys
::sysinit:/bin/mount -t devtmpfs devtmpfs /dev
console::respawn:-/bin/sh
5.3 rcS 启动脚本
5.3.1 基本结构
#!/bin/sh
# /etc/init.d/rcS - 系统启动脚本
# 1. 挂载虚拟文件系统(如果 inittab 中未挂载)
mount -t proc proc /proc 2>/dev/null || true
mount -t sysfs sysfs /sys 2>/dev/null || true
mount -t tmpfs tmpfs /tmp 2>/dev/null || true
mount -t devtmpfs devtmpfs /dev 2>/dev/null || true
# 2. 设置系统参数
echo "Setting kernel parameters..."
echo 1 > /proc/sys/kernel/printk # 减少内核日志
# 3. 初始化设备
echo "Initializing devices..."
mdev -s # 静态设备扫描
echo /sbin/mdev > /proc/sys/kernel/hotplug # 热插拔支持
# 4. 配置网络
echo "Configuring network..."
/etc/init.d/network start
# 5. 启动服务
echo "Starting services..."
/etc/init.d/syslog start
# 6. 完成
echo "System initialization complete."
5.3.2 完整 rcS 脚本
#!/bin/sh
# /etc/init.d/rcS - 完整系统启动脚本
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH
echo "========================================"
echo " BusyBox Mini Rootfs - Starting..."
echo "========================================"
# --------------------------------------------------------
# 1. 文件系统挂载
# --------------------------------------------------------
echo "[1/7] Mounting filesystems..."
mount_proc() {
mount -t proc proc /proc 2>/dev/null && echo " /proc mounted"
}
mount_sys() {
mount -t sysfs sysfs /sys 2>/dev/null && echo " /sys mounted"
}
mount_tmp() {
mount -t tmpfs -o size=64M tmpfs /tmp 2>/dev/null && echo " /tmp mounted"
}
mount_dev() {
if ! mount -t devtmpfs devtmpfs /dev 2>/dev/null; then
echo " devtmpfs not available, creating static devices"
mknod /dev/console c 5 1 2>/dev/null
mknod /dev/null c 1 3 2>/dev/null
mknod /dev/zero c 1 5 2>/dev/null
mknod /dev/tty c 5 0 2>/dev/null
else
echo " /dev mounted"
fi
}
mount_proc
mount_sys
mount_tmp
mount_dev
# --------------------------------------------------------
# 2. 设备初始化
# --------------------------------------------------------
echo "[2/7] Initializing devices..."
# mdev 设备管理器
if [ -x /sbin/mdev ]; then
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
echo " mdev initialized"
fi
# 创建标准符号链接
[ -e /dev/fd ] || ln -sf /proc/self/fd /dev/fd
[ -e /dev/stdin ] || ln -sf /proc/self/fd/0 /dev/stdin
[ -e /dev/stdout ] || ln -sf /proc/self/fd/1 /dev/stdout
[ -e /dev/stderr ] || ln -sf /proc/self/fd/2 /dev/stderr
# --------------------------------------------------------
# 3. 系统配置
# --------------------------------------------------------
echo "[3/7] Configuring system..."
# 主机名
if [ -f /etc/hostname ]; then
hostname -F /etc/hostname
echo " hostname: $(hostname)"
fi
# 时区
if [ -f /etc/TZ ]; then
export TZ=$(cat /etc/TZ)
fi
# 交换文件/分区
if [ -f /var/swap ]; then
swapon /var/swap 2>/dev/null && echo " swap enabled"
fi
# --------------------------------------------------------
# 4. 网络配置
# --------------------------------------------------------
echo "[4/7] Configuring network..."
# 回环接口
ifconfig lo 127.0.0.1 up 2>/dev/null && echo " lo: 127.0.0.1"
# 以太网接口(DHCP)
if [ -x /sbin/udhcpc ]; then
udhcpc -i eth0 -b -q -R 2>/dev/null && echo " eth0: DHCP started"
elif [ -f /etc/network/interfaces ]; then
# 静态 IP 配置
source /etc/network/interfaces
ifconfig eth0 $IP netmask $NETMASK up
route add default gw $GATEWAY
echo " eth0: $IP"
fi
# DNS
if [ -f /etc/resolv.conf ]; then
echo " DNS configured"
fi
# --------------------------------------------------------
# 5. 系统日志
# --------------------------------------------------------
echo "[5/7] Starting system logging..."
if [ -x /sbin/syslogd ]; then
syslogd -n -O /var/log/messages &
echo " syslogd started"
fi
if [ -x /sbin/klogd ]; then
klogd -n &
echo " klogd started"
fi
# --------------------------------------------------------
# 6. 服务启动
# --------------------------------------------------------
echo "[6/7] Starting services..."
# 启动 /etc/init.d 下的其他脚本
for script in /etc/init.d/S[0-9][0-9]*; do
[ -x "$script" ] || continue
echo " Starting $(basename $script)..."
$script start
done
# --------------------------------------------------------
# 7. 完成
# --------------------------------------------------------
echo "[7/7] System initialization complete."
echo "========================================"
echo " BusyBox $(busybox | head -1 | awk '{print $2}')"
echo " $(date)"
echo "========================================"
5.3.3 网络配置文件
# /etc/network/interfaces
IP=192.168.1.100
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
DNS=8.8.8.8
5.3.4 服务脚本模板
#!/bin/sh
# /etc/init.d/S10myapp - 应用服务脚本
DAEMON=/usr/bin/myapp
PIDFILE=/var/run/myapp.pid
case "$1" in
start)
echo "Starting myapp..."
$DAEMON &
echo $! > $PIDFILE
;;
stop)
echo "Stopping myapp..."
kill $(cat $PIDFILE 2>/dev/null) 2>/dev/null
rm -f $PIDFILE
;;
restart)
$0 stop
sleep 1
$0 start
;;
status)
if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE) 2>/dev/null; then
echo "myapp is running (PID: $(cat $PIDFILE))"
else
echo "myapp is not running"
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
5.4 运行级别
5.4.1 运行级别概念
传统 Linux 系统定义了 7 个运行级别(runlevel):
| 级别 | 说明 | BusyBox 支持 |
|---|
| 0 | 关机 | ✓ |
| 1 | 单用户模式 | ✓ |
| 2 | 多用户(无网络) | ✓ |
| 3 | 多用户(有网络) | ✓ |
| 4 | 未定义/自定义 | ✓ |
| 5 | 图形界面 | ✓ |
| 6 | 重启 | ✓ |
注意:BusyBox init 实际上忽略 inittab 中的 runlevel 字段。所有条目都会被执行。
5.4.2 BusyBox 中的运行级别处理
# BusyBox init 不支持 telinit 切换运行级别
# 如需类似功能,使用脚本模拟
# /etc/init.d/runlevel
#!/bin/sh
case "$1" in
1) # 单用户
/etc/init.d/S10network stop
/etc/init.d/S20syslog stop
;;
3) # 多用户
/etc/init.d/S10network start
/etc/init.d/S20syslog start
;;
6) # 重启
reboot
;;
0) # 关机
halt
;;
esac
5.5 启动流程详解
5.5.1 完整启动时序
时间线:
T0 ──── 内核启动
│
T1 ──── 内核初始化硬件
│
T2 ──── 内核挂载 initramfs/rootfs
│
T3 ──── 内核执行 /sbin/init(BusyBox init)
│
T4 ──── init 读取 /etc/inittab
│
T5 ──── init 执行所有 sysinit 条目
│ ├── 挂载 /proc
│ ├── 挂载 /sys
│ ├── 挂载 /tmp
│ ├── 挂载 /dev
│ └── 执行 /etc/init.d/rcS
│
T6 ──── rcS 脚本执行
│ ├── 初始化设备
│ ├── 配置网络
│ ├── 启动日志
│ └── 启动服务
│
T7 ──── init 等待所有 sysinit 完成
│
T8 ──── init 启动 respawn/askfirst 条目
│ ├── 启动 console shell
│ └── 启动其他终端
│
T9 ──── 系统就绪,用户可以登录
5.5.2 使用 bootchartd 分析启动
# 启用 bootchartd
# 内核启动参数:
init=/sbin/bootchartd
# 或在 inittab 中
::sysinit:/sbin/bootchartd start
# bootchartd 会生成 /var/log/bootchart.tgz
# 使用 pybootchartgui 分析
$ pybootchartgui /var/log/bootchart.tgz
5.5.3 查看启动日志
# 查看内核启动日志
$ dmesg | head -20
[ 0.000000] Linux version 5.15.0 ...
[ 0.000000] Command line: console=ttyS0 ...
[ 0.000000] BIOS-provided physical RAM map:
...
# 查看 BusyBox init 启动日志(如果有 syslogd)
$ cat /var/log/messages | head -20
Jan 1 10:00:00 miniroot syslog.info syslogd started
Jan 1 10:00:00 miniroot user.notice kernel: Starting system...
5.6 inittab 高级配置
5.6.1 多串口配置
# /etc/inittab - 多串口设备
# 主串口(调试)
ttyS0::respawn:-/bin/sh
# 第二串口(连接外部设备)
ttyS1::respawn:-/bin/sh
# USB 串口
ttyUSB0::respawn:-/bin/sh
# 带登录认证的串口
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 -n -l /bin/login
5.6.2 自动登录配置
# 方式一:直接启动 shell(无密码)
console::respawn:-/bin/sh
# 方式二:使用 getty 自动登录
console::respawn:/sbin/getty -L console 115200 vt100 -n -l /bin/login
# 方式三:修改 /bin/login 实现自动登录(不推荐)
# 在 rcS 中添加:
exec /bin/login -f root
5.6.3 嵌入式应用启动
# /etc/inittab - 嵌入式设备配置
# 系统初始化
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -t sysfs sysfs /sys
::sysinit:/bin/mount -t devtmpfs devtmpfs /dev
::sysinit:/etc/init.d/rcS
# 启动主应用程序(前台运行)
console::respawn:-/usr/bin/myapp
# 如果 myapp 崩溃,5 秒后重启
console::respawn:/bin/sh -c "sleep 5; /usr/bin/myapp"
5.6.4 看门狗配置
# /etc/inittab - 带看门狗
# 启动看门狗
::sysinit:/sbin/watchdog -t 30 /dev/watchdog
# 看门狗喂狗脚本
# 在 /etc/init.d/rcS 中添加:
# while true; do
# watchdog -t 5 /dev/watchdog
# sleep 15
# done &
# 关机时停止看门狗
::shutdown:/sbin/watchdog -t 0 /dev/watchdog
5.7 替代 init 系统
5.7.1 使用 BusyBox 的简单 init
# 不使用 inittab,直接在内核参数中指定
# 内核参数:
init=/bin/sh
# 系统启动后直接进入 shell(无 init 管理)
5.7.2 使用 runit
# runit 是另一个轻量级 init 系统
# 可以与 BusyBox 配合使用
# /etc/runit/1 - 系统初始化
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
# /etc/runit/2 - 运行服务
#!/bin/sh
exec runsvdir -P /etc/runit/runsvdir/default
# /etc/runit/runsvdir/default/
# ├── sshd/run
# ├── syslogd/run
# └── myapp/run
5.8 调试 init 问题
5.8.1 常见错误
# 错误:Kernel panic - No working init found
# 原因:init 符号链接不存在或路径错误
$ ls -la /sbin/init
ls: /sbin/init: No such file or directory
# 解决:
$ ln -s ../bin/busybox /sbin/init
# 错误:init: bad inittab entry
# 原因:inittab 格式错误
# 检查:
$ busybox init --help
# 确认格式:<id>:<runlevels>:<action>:<process>
# 常见错误:
# - 缺少冒号分隔符
# - action 拼写错误
# - process 路径不存在
5.8.2 调试技巧
# 在内核参数中启用详细日志
# 内核参数:
loglevel=7 initcall_debug
# 手动测试 inittab
$ mkdir -p /tmp/test_init
$ cat > /tmp/test_init/inittab << 'EOF'
console::respawn:-/bin/sh
EOF
# 使用 strace 跟踪 init
$ strace -f -o /tmp/init.log /sbin/init
# 查看 init 错误日志
$ dmesg | grep init
5.8.3 恢复模式
# 当系统无法正常启动时
# 在内核参数中添加:
init=/bin/sh
# 系统会直接进入 shell,然后手动修复
$ mount -t proc proc /proc
$ mount -t sysfs sysfs /sys
$ mount -o remount,rw /
$ vi /etc/inittab # 修复配置
$ exec /sbin/init # 重启 init
5.9 本章小结
| 概念 | 说明 |
|---|
| inittab | BusyBox init 的配置文件 |
| sysinit | 系统初始化时执行的动作 |
| respawn | 进程退出后自动重启 |
| rcS | 系统启动脚本,执行初始化任务 |
| mdev | BusyBox 的设备管理器 |
| bootchartd | 启动性能分析工具 |
扩展阅读
上一章: 第 4 章 — rootfs 概念与构建
下一章: 第 6 章 — 网络工具