4. 构建最小根文件系统:使用 BusyBox 制作根文件系统、创建 initramfs

内核启动后必须挂载根文件系统才能完成用户态初始化。在 QEMU + kgdb 调试场景中,initramfs(基于内存的临时根文件系统)是最轻量、最可控的方案。本章使用 BusyBox 编译一个极简根文件系统,并将其打包为 initramfs,为后续内核断点调试提供完整的用户态环境。

目标:生成一个体积小于 2MB、包含 /init 入口、/bin/sh 及基本调试工具(ls、cat、mount、insmod)的 initramfs.cpio.gz 文件。

4.1 整体流程概览

构建最小根文件系统分为四个阶段:编译 BusyBox → 创建目录骨架 → 配置设备节点与 init 脚本 → 打包为 initramfs。下图展示了完整的构建流水线:

BusyBox 根文件系统构建流程 ① 编译 BusyBox make defconfig ② 创建目录骨架 bin/ sbin/ etc/ dev/ ③ 配置 init 脚本 /init + rcS ④ 打包 initramfs cpio + gzip initramfs.cpio.gz ~1.5 MB initramfs 直接嵌入内核或由 QEMU -initrd 参数加载,无需磁盘驱动 kgdb 调试期间可随时修改 init 脚本并重新打包,迭代极快

4.2 编译静态链接的 BusyBox

BusyBox 是一个集成了数百条 Linux 命令的单一可执行文件。静态链接可以避免在 initramfs 中携带动态库,大幅减小体积。

步骤命令 / 操作说明
1wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2下载源码
2tar xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1解压并进入目录
3make defconfig使用默认配置
4make menuconfig进入配置界面
5设置 Settings → Build static binary (no shared libs) 为 [*]开启静态编译
6设置 Linux Module Utilities → Simplified modutils 为 [*]支持 insmod/modprobe
7make -j$(nproc) && make install编译并安装到 _install/
验证:编译完成后检查 busybox 是否为静态链接:
file _install/bin/busybox 应输出 ELF 64-bit LSB executable, statically linked。

4.3 构建根文件系统目录骨架

在 _install 基础上补充必要的目录和设备节点:

cd _install
mkdir -p proc sys dev etc/init.d tmp mnt
chmod 1777 tmp

# 创建基本设备节点
sudo mknod -m 666 dev/null c 1 3
sudo mknod -m 600 dev/console c 5 1
sudo mknod -m 666 dev/tty c 5 0
sudo mknod -m 666 dev/ttyS0 c 4 64
sudo mknod -m 644 dev/mmcblk0 b 179 0   # 可选:模拟块设备
目录 / 文件用途
bin/BusyBox 命令软链接(ls、cat、mount 等)
sbin/系统管理命令(init、insmod、ifconfig)
dev/设备节点(console、null、ttyS0)
etc/init.d/启动脚本 rcS
proc/挂载点:proc 文件系统
sys/挂载点:sysfs 文件系统
tmp/临时文件(1777 权限)

4.4 编写 /init 与 rcS 启动脚本

/init 是 initramfs 的入口,内核启动后第一个执行的可执行文件。以下是一个面向 kgdb 调试场景的最小化实现:

#!/bin/sh

# 挂载必要的虚拟文件系统
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp

# 创建设备节点(如果 devtmpfs 未自动挂载)
mdev -s

# 打印内核版本与调试信息
echo "=== kgdb minimal rootfs ==="
cat /proc/version
echo "==========================="

# 启动 shell(kgdb 断点可在此处触发)
exec /bin/sh

同时创建 etc/init.d/rcS 作为辅助脚本(可选):

#!/bin/sh
echo "rcS: starting system services..."
# 可在此处插入 insmod 加载调试模块
权限注意:/init 和 etc/init.d/rcS 必须具有可执行权限:
chmod +x _install/init _install/etc/init.d/rcS

4.5 打包为 initramfs.cpio.gz

使用 cpio 工具将整个根文件系统目录打包为 gzip 压缩的 initramfs 镜像:

cd _install
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
cd ..
ls -lh initramfs.cpio.gz

打包后的文件大小通常在 1.2MB ~ 1.8MB 之间,适合通过 QEMU 的 -initrd 参数加载。

4.6 在 QEMU 中验证 initramfs

使用上一章编译的内核与本章制作的 initramfs 启动 QEMU:

qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a57 \
  -kernel arch/arm64/boot/Image \
  -initrd initramfs.cpio.gz \
  -nographic \
  -append "console=ttyAMA0"

启动后应直接进入 BusyBox shell,输入 ls /bin 可看到所有命令列表。此时内核已成功挂载 initramfs 并执行了 /init。

kgdb 调试准备:在 QEMU 启动参数中加入 -s -S 即可暂停内核于第一条指令,随后使用 gdb 连接并设置断点。initramfs 中的 /init 脚本是设置早期断点的理想位置。

4.7 常见问题与排查

现象原因解决方案
Kernel panic: VFS: Unable to mount root fsinitramfs 格式错误或未找到确认 -initrd 路径正确;检查 cpio 打包命令
sh: can't access tty; job control turned off缺少 dev/console 或 dev/tty使用 mknod 创建 console 和 tty 设备节点
init: must be run as PID 1/init 权限不足或 shebang 错误检查 /init 可执行权限;确认第一行为 #!/bin/sh
BusyBox 命令缺失静态编译未开启或安装不完整重新执行 make menuconfig 确认静态链接选项

4.8 本章小结

通过本章操作,你已掌握:

  • 使用 BusyBox 编译静态链接的最小根文件系统
  • 构建包含 /init、设备节点和虚拟文件系统挂载点的目录骨架
  • 编写面向 kgdb 调试场景的 init 启动脚本
  • 使用 cpio + gzip 打包 initramfs 并在 QEMU 中验证