从零构建 BusyBox 最小根文件系统:init 进程、inittab 与 mdev 热插拔机制详解

从零构建 BusyBox 最小根文件系统:init 进程、inittab 与 mdev 热插拔机制详解
从零构建 BusyBox 最小根文件系统init 进程、inittab 与 mdev 热插拔机制详解一、内核启动后的真空地带没有 rootfs 就没有用户空间Linux 内核启动的最后一步是尝试挂载根文件系统并执行/sbin/init。如果 rootfs 缺失或 init 程序不存在内核会陷入 kernel panic输出标志性的错误信息No working init found. Try passing init option to kernel. Kernel panic - not syncing: Attempted to kill init!在嵌入式开发中尤其是为一块全新的 ARM 开发板做 BSP 移植时第一步往往不是写驱动而是构建一个能正确启动到 shell 提示符的最小根文件系统。BusyBox 被称为嵌入式 Linux 的瑞士军刀——它把init、sh、mount、ls、cp等上百个常用工具的实现在一个二进制文件中提供。本文完整记录从交叉编译 BusyBox 到构建可启动的 rootfs 镜像的全过程。二、从 Kernel 到 Shell 的启动链路init 进程与 inittab 的协同机制Linux 启动到用户空间的第一个进程 PID1 就是 init。BusyBox 的 init 实现了 SystemV 风格的精简版其行为完全由/etc/inittab配置文件驱动。sequenceDiagram participant Kernel as Linux Kernel participant Init as /sbin/init(PID1) participant Dev as /dev 设备节点 participant RC as /etc/init.d/rcS participant Shell as /bin/sh(控制台) Kernel-Init: 1. kernel_init() → run_init_process(/sbin/init) Note over Init: 2. 解析 /etc/inittab Init-Init: 3. sysinit: 执行 /etc/init.d/rcS Init-Dev: 4. mdev -s 扫描 /sys 创建设备节点 Init-Dev: 5. echo /sbin/mdev hotplug Init-RC: 6. rcS 中挂载 proc, sysfs, tmpfs Init-Shell: 7. respawn: /bin/sh (控制台登录) Shell-Shell: 8. 用户交互等待 Note over Init,Shell: 若 shell 退出,respawn 自动重启inittab的格式是id:runlevel:action:process。实际的最小配置如下# /etc/inittab —— BusyBox init 配置文件 # 格式: id:runlevel:action:process # 系统初始化脚本启动时执行一次等待完成 ::sysinit:/etc/init.d/rcS # 控制台 shell退出后自动重启 respawn ::respawn:-/bin/sh # CtrlAltDel 重启 ::ctrlaltdel:/sbin/reboot # 关机时卸载文件系统 ::shutdown:/bin/umount -a -r三、生产级构建流程交叉编译 BusyBox 与生成 ext4 镜像以下步骤建立在一台 x86_64 Linux 宿主机上目标板为 ARM Cortex-Aarm-linux-gnueabihf 工具链。Step 1 — 交叉编译 BusyBox# 下载 BusyBox以 1.36.1 为例 wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 tar xjf busybox-1.36.1.tar.bz2 cd busybox-1.36.1 # 使用默认配置并调整 make defconfig ARCHarm CROSS_COMPILEarm-linux-gnueabihf- # 关键配置项可通过 make menuconfig 图形化配置 # Settings → Build Options → Build static binary (no shared libs) → [*] # Settings → Installation Options → Dont use /usr → [*] # Coreutils → sync → [*] # Linux System Utilities → mount → [*] # Linux System Utilities → mdev → [*] 支持设备热插拔管理 # 静态编译——避免依赖目标板的动态库 make -j$(nproc) ARCHarm CROSS_COMPILEarm-linux-gnueabihf- make install CONFIG_PREFIX../rootfs ARCHarm CROSS_COMPILEarm-linux-gnueabihf-Step 2 — 构建文件系统目录结构#!/bin/bash # build_rootfs.sh —— 构建最小 rootfs 目录结构 # 必须包含 FHS 3.0 标准要求的最小目录集 ROOTFS_DIR./rootfs # 创建必要目录FHS 规范 mkdir -p ${ROOTFS_DIR}/{bin,sbin,etc,proc,sys,dev,tmp,lib,usr/{bin,sbin,lib},var/{run,log}} # 创建 rcS 初始化脚本 cat ${ROOTFS_DIR}/etc/init.d/rcS SCRIPT #!/bin/sh # /etc/init.d/rcS —— 系统启动初始化脚本 # 由 inittab 中的 sysinit action 调用 echo BusyBox RootFS Starting # 1. 挂载虚拟文件系统必须在创建设备节点之前 mount -t proc none /proc mount -t sysfs none /sys mount -t tmpfs none /tmp mount -t devpts none /dev/pts # 2. 挂载调试文件系统可选调试用 mount -t debugfs none /sys/kernel/debug # 3. 配置主机名 hostname emb-linux echo emb-linux /proc/sys/kernel/hostname # 4. mdev 设备管理——冷插拔扫描 # -s: 系统启动时扫描 /sys/class 和 /sys/block # 为所有已有设备创建 /dev 下的设备节点 mdev -s # 5. 热插拔事件处理——内核通过 netlink 通知 mdev # 将 mdev 路径写入 hotplug 节点后 # 内核在设备增删时会自动调用 mdev echo /sbin/mdev /proc/sys/kernel/hotplug echo RootFS Initialization Complete SCRIPT chmod x ${ROOTFS_DIR}/etc/init.d/rcS # 创建 inittab cat ${ROOTFS_DIR}/etc/inittab CFG ::sysinit:/etc/init.d/rcS ::respawn:-/bin/sh ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r CFG # 复制必要的 C 运行时库如果不是静态编译则需要 # 静态编译可跳过此步骤 echo [INFO] rootfs 目录结构已创建: ${ROOTFS_DIR}Step 3 — 生成 ext4 镜像# 创建 64MB 的 ext4 镜像 # 实际大小根据 BusyBox 编译产物调整静态编译约 1.5MB dd if/dev/zero ofrootfs.ext4 bs1M count64 mkfs.ext4 -L rootfs rootfs.ext4 # 挂载并复制文件 sudo mount rootfs.ext4 /mnt sudo cp -a ./rootfs/* /mnt/ sudo umount /mnt四、mdev 设备管理的边界与 udev 的能力差距与选择依据mdev 是 BusyBox 内置的迷你版 udev差距主要在功能范围上特性mdevudev/systemd-udevd设备节点创建支持基于 /sys支持热插拔事件处理支持通过 hotplug支持通过 netlink自定义命名规则有限通过 /etc/mdev.conf完整的规则引擎固件加载手动脚本内置支持二进制体积 5KB约 1.5MB适用场景嵌入式、单板系统桌面、服务器对于不使用动态内核模块的固定硬件平台如定制 IoT 网关mdev 完全足够。但如果系统需要 USB 设备的热插拔固件加载或基于设备属性的自定义节点命名应考虑构建完整 udev。Init 进程的场景同样有选择——BusyBox init vs systemd。前者足够应对嵌入式场景后者功能完备但镜像体积约 20 倍增长。五、总结从零构建 BusyBox 最小根文件系统的核心环节包括静态交叉编译 BusyBox避免库依赖、按 FHS 规范创建目录树、配置 inittab 驱动 init 行为、利用 mdev 的冷热插拔机制管理设备节点。整个过程产出的 rootfs 镜像通常在 3MB 以内可直接作为嵌入式 Linux BSP 的基础 rootfs。后续扩展通常沿着两个方向按需追加 glibc 动态库以支持更复杂的应用程序或将 ext4 替换为 squashfs 实现只读根文件系统以增强可靠性。