强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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轻量、兼容 SysVGentoo、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系统关机时执行
restartinit 收到 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 本章小结

概念说明
inittabBusyBox init 的配置文件
sysinit系统初始化时执行的动作
respawn进程退出后自动重启
rcS系统启动脚本,执行初始化任务
mdevBusyBox 的设备管理器
bootchartd启动性能分析工具

扩展阅读


上一章: 第 4 章 — rootfs 概念与构建
下一章: 第 6 章 — 网络工具