Skip to content

为 QEMU 编译一个干净的 Linux 内核

上一篇 WSL 里全量编译 Linux 内核 用的是 allmodconfig——把所有模块全打开,验证工具链完整性。这次目标不同:编译一个能在 QEMU 里跑起来的精简内核,为后面的 BusyBox rootfs 和最小系统做准备。

核心区别在于配置策略。allmodconfig 产出 41 MB 的 bzImage 和一万多个 .ko 模块,启动时跑几十秒的自检测试,最后还可能因为 KASAN 触发 page fault 直接 panic。而 defconfig 只启用最常用的选项,产出 14 MB 的 bzImage,0.6 秒启动完毕。

为什么不用之前编译好的内核

之前 allmodconfig 编译的内核有几个问题:

问题原因影响
bzImage 41 MBKASAN + 全量调试代码加载慢
启动 66 秒还没完lockdep 395 个测试 + ftrace 自检 + RCU 自检不可用
Kernel PanicKASAN 检测到 page fault直接挂
配置选项 4929 个allmodconfig 全开远超 QEMU 需要

allmodconfig 的价值在于验证工具链——一万多个模块都编译通过说明环境没问题。但要在 QEMU 里实际跑一个最小系统,需要一个干净的 defconfig 内核。

编译过程

源码沿用之前下载的 Linux 6.18.19,不需要重新下载解压。

生成默认配置

bash
cd ~/build/linux-6.18.19
make defconfig

defconfig 根据当前架构(x86_64)生成一套合理的默认配置。和 allmodconfig 的对比:

配置启用选项数KASANftrace 测试lockdep 测试
allmodconfig4929开启开启开启
defconfig1621关闭关闭关闭

可以检查一下关键选项:

bash
grep 'CONFIG_KASAN\|CONFIG_FTRACE_STARTUP_TEST\|CONFIG_DEBUG_KERNEL' .config
CONFIG_DEBUG_KERNEL=y
# CONFIG_KASAN is not set
# CONFIG_FTRACE_STARTUP_TEST is not set

CONFIG_DEBUG_KERNEL=y 保留没问题,它只是一个总开关,不会单独造成性能影响。真正重的是 KASAN、lockdep 这些子选项,defconfig 默认都是关的。

有一个选项值得关注:

bash
grep 'CONFIG_KVM' .config
CONFIG_KVM_GUEST=y
# CONFIG_KVM is not set

CONFIG_KVM_GUEST=y 是客户机优化——让内核知道自己运行在 KVM 虚拟机里,启用 kvmclock 等 paravirt 接口,减少虚拟化开销。这个 defconfig 自动帮你开好了,不需要手动配置。

CONFIG_KVM is not set 表示不编译 KVM 宿主机功能——我们的迷你 Linux 不需要在里面再开虚拟机。

编译

bash
make -j$(nproc) bzImage

32 核并行编译,83 秒完成。只编译 bzImage 不编译模块,因为 initramfs 方案不需要 .ko 文件。

Kernel: arch/x86/boot/bzImage is ready  (#2)

产物对比

产物allmodconfigdefconfig
bzImage40.7 MB14 MB
编译时间(32核)~45 分钟~83 秒
启动时间(QEMU)66 秒(panic)0.6 秒

14 MB 的 bzImage 包含了完整的内核功能——网络协议栈、文件系统、设备驱动、进程调度,足够支撑一个功能完整的 Linux 系统。

bzImage 是什么

bzImage 的名字容易误解——bz 不是 bzip2,而是 "big zImage"。它是一个自解压的内核映像:

bzImage 文件结构:
┌─────────────────────┐
│ Boot Sector (512B)  │ ← BIOS 引导头 + Linux boot protocol 头
├─────────────────────┤
│ Setup Code          │ ← 实模式初始化代码
├─────────────────────┤
│ 解压缩代码          │ ← 一小段解压程序
├─────────────────────┤
│ 压缩的内核          │ ← vmlinux 经过 gzip/lz4/zstd 压缩
└─────────────────────┘

引导器(GRUB/QEMU)把 bzImage 加载到内存后,执行流程:

  1. 运行 Boot Sector 的 setup 代码,切换到保护模式
  2. 运行解压缩代码,将压缩的内核解压到内存
  3. 跳转到解压后的内核入口(start_kernel()

所以 bzImage 本质上是一个"自带解压器的压缩内核"。14 MB 的 bzImage 解压后是完整的内核二进制。

下一步

内核准备好了,接下来需要一个根文件系统(rootfs)。内核启动完毕后,它会去找 /init 程序来运行——没有 rootfs 的内核启动到最后会 panic:

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

下一篇用 BusyBox 构建这个 rootfs。

最后更新于:

Hosted by GitHub Pages