基于 BusyBox 快速制作内核验证环境

本文介绍的方法,旨在利用基于 BusyBox 制作的简易文件系统来快速启动内核并进入一个 shell 环境,以此来验证内核的功能和稳定性。其优点在于制作简单,资源占用小,验证环境的启动时间短(仅需启动内核,省去了各种用户态应用及框架的启动过程);缺点是可拓展性较差,难以支撑一些需要用到用户态工具的复杂内核功能的验证。

本文主要以 ARM64 为例讲解整个过程。实际操作中,应结合实际使用的体系结构与工具链对步骤细节进行调整。介绍的验证环境有基于 initrd 和基于 SD 卡两种,可以根据实际情况选择。

BusyBox 编译

 1# BusyBox 源代码下载,解压
 2# https://busybox.net/downloads/
 3
 4cd path/to/busybox
 5
 6# 配置
 7make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
 8# 选中以下配置:
 9#   Build Settings --->
10#       [*] Build static binary (no shared libs)
11
12# 编译
13make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j32
14
15# 安装
16make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- install
17
18# 编译生成的文件在:./_install

制作根文件系统

 1mkdir rootfs
 2pushd rootfs
 3
 4cp -r /path/to/busybox/_install/* .
 5mkdir -p usr lib root proc sys etc/init.d dev/pts
 6cat > etc/init.d/rcS <<- EOF
 7	mount -t proc none /proc
 8	mount -t sysfs none /sys
 9	/sbin/mdev -s
10	mount -t devpts devpts /dev/pts
11
12	# QEMU_HOST_IP=10.0.2.2
13	ip addr add 10.0.2.3/24 dev eth0
14	ip link set eth0 up
15EOF
16chmod 755 etc/init.d/rcS
17
18pushd dev
19mknod -m 666 tty1 c 4 1
20mknod -m 666 tty2 c 4 2
21mknod -m 666 tty3 c 4 3
22mknod -m 666 tty4 c 4 4
23mknod -m 666 console c 5 1
24mknod -m 666 null c 1 3
25popd # dev
26
27# linuxrc, a symlink to bin/busybox, is one of the outcome of BusyBox
28# compilation. Take it as init to let the kernel run it after kernel's
29# initialization.
30mv linuxrc init

以 initrd 为根文件系统,制作 initrd 镜像:

1find . | cpio -H newc -o --quiet | gzip -c --quiet > ../image.cpio.gz
2popd # rootfs

以 SD 卡设备为根文件系统,制作 ext3 文件系统镜像:

1popd # rootfs
2
3dd if=/dev/zero of=rootfs.sd bs=1M count=32
4mkfs.ext3 rootfs.sd
5sudo mount -t ext3 -o loop rootfs.sd /mnt
6sudo cp -r rootfs/* /mnt
7sudo umount /mnt

内核编译

initrd 是基于内存的文件系统,以 initrd 为根文件系统不需要设备驱动,因此也就不需要编译内核模块。

1make distclean
2make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
3make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image -j8

其他体系结构的内核编译命令,供参考:

1# arm
2make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
3make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- Image -j8
4
5# x86
6make x86_64_defconfig
7make bzImage -j32

而对于根文件系统为 SD 卡设备的场景,则稍微复杂一点。由于不同开发板关于 SD 卡设备的属性不同,在编译内核时需要选择与硬件向匹配的 config,并且编译 dtb。以 arm 的 vexpress 平台为例:

1make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
2make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j32
3make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules
4make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs

启动 QEMU 虚拟机

这篇 QEMU Wiki 所述,如果你无意模拟特定的运行环境或外围设备,仅仅只是想跑一下 Linux 内核,那你直接用这个平台就好:

1-M virt

查看该平台上可以模拟的 CPU 型号:

1qemu-system-aarch64 -M virt -cpu help

这里我们就用这个平台,并指定 CPU 为 cortex-a57(可通过不指定来选择默认 CPU 型号)。

1qemu-system-aarch64 \
2	-M virt \
3	-cpu cortex-a57 \
4	-m 1024M \
5	-kernel arch/arm64/boot/Image \
6	-initrd ../image.cpio.gz \
7	-nographic \
8	-append "console=ttyAMA0"

其他体系结构,供参考:

 1# arm
 2qemu-system-arm \
 3	-M virt \
 4	-kernel arch/arm/boot/zImage \
 5	-initrd image.cpio.gz \
 6	-nographic \
 7	-append "console=ttyAMA0"
 8
 9# x86
10qemu-system-x86_64 \
11	-m 1024M \
12	-kernel arch/x86_64/boot/bzImage \
13	-initrd ../image64.cpio.gz \
14	-nographic \
15	-append "console=ttyS0"

若是想模拟从 SD 卡启动,则需要使用一个带 SD 卡设备的平台,指定相应的 dtb 文件(-dtb)和模拟 SD 卡内容的文件系统镜像(-sd),并在内核启动参数中指明根文件系统所处的硬件设备(root=/dev/...)。以 arm 的 vexpress 平台为例:

1qemu-system-arm \\
2	-M vexpress-a9 \\
3	-m 1024M \\
4	-nographic \\
5	-kernel arch/arm/boot/zImage \\
6	-dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \\
7	-sd /home/rootfs.sd \\
8	-append "root=/dev/mmcblk0 rw console=ttyAMA0"

参考资料


“7/28 XX 版本容器 at 大面积失败问题”分析报告
一个由动态库对其他动态库的依赖导致的问题