4. 构建最小根文件系统:使用 BusyBox 制作根文件系统、创建 initramfs
内核启动后必须挂载根文件系统才能完成用户态初始化。在 QEMU + kgdb 调试场景中,initramfs(基于内存的临时根文件系统)是最轻量、最可控的方案。本章使用 BusyBox 编译一个极简根文件系统,并将其打包为 initramfs,为后续内核断点调试提供完整的用户态环境。
目标:生成一个体积小于 2MB、包含
/init 入口、/bin/sh 及基本调试工具(ls、cat、mount、insmod)的 initramfs.cpio.gz 文件。
4.1 整体流程概览
构建最小根文件系统分为四个阶段:编译 BusyBox → 创建目录骨架 → 配置设备节点与 init 脚本 → 打包为 initramfs。下图展示了完整的构建流水线:
4.2 编译静态链接的 BusyBox
BusyBox 是一个集成了数百条 Linux 命令的单一可执行文件。静态链接可以避免在 initramfs 中携带动态库,大幅减小体积。
| 步骤 | 命令 / 操作 | 说明 |
|---|---|---|
| 1 | wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 | 下载源码 |
| 2 | tar xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1 | 解压并进入目录 |
| 3 | make defconfig | 使用默认配置 |
| 4 | make menuconfig | 进入配置界面 |
| 5 | 设置 Settings → Build static binary (no shared libs) 为 [*] | 开启静态编译 |
| 6 | 设置 Linux Module Utilities → Simplified modutils 为 [*] | 支持 insmod/modprobe |
| 7 | make -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 fs | initramfs 格式错误或未找到 | 确认 -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 中验证