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

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 章 — 网络工具