为 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 MB | KASAN + 全量调试代码 | 加载慢 |
| 启动 66 秒还没完 | lockdep 395 个测试 + ftrace 自检 + RCU 自检 | 不可用 |
| Kernel Panic | KASAN 检测到 page fault | 直接挂 |
| 配置选项 4929 个 | allmodconfig 全开 | 远超 QEMU 需要 |
allmodconfig 的价值在于验证工具链——一万多个模块都编译通过说明环境没问题。但要在 QEMU 里实际跑一个最小系统,需要一个干净的 defconfig 内核。
编译过程
源码沿用之前下载的 Linux 6.18.19,不需要重新下载解压。
生成默认配置
cd ~/build/linux-6.18.19
make defconfigdefconfig 根据当前架构(x86_64)生成一套合理的默认配置。和 allmodconfig 的对比:
| 配置 | 启用选项数 | KASAN | ftrace 测试 | lockdep 测试 |
|---|---|---|---|---|
| allmodconfig | 4929 | 开启 | 开启 | 开启 |
| defconfig | 1621 | 关闭 | 关闭 | 关闭 |
可以检查一下关键选项:
grep 'CONFIG_KASAN\|CONFIG_FTRACE_STARTUP_TEST\|CONFIG_DEBUG_KERNEL' .configCONFIG_DEBUG_KERNEL=y
# CONFIG_KASAN is not set
# CONFIG_FTRACE_STARTUP_TEST is not setCONFIG_DEBUG_KERNEL=y 保留没问题,它只是一个总开关,不会单独造成性能影响。真正重的是 KASAN、lockdep 这些子选项,defconfig 默认都是关的。
有一个选项值得关注:
grep 'CONFIG_KVM' .configCONFIG_KVM_GUEST=y
# CONFIG_KVM is not setCONFIG_KVM_GUEST=y 是客户机优化——让内核知道自己运行在 KVM 虚拟机里,启用 kvmclock 等 paravirt 接口,减少虚拟化开销。这个 defconfig 自动帮你开好了,不需要手动配置。
CONFIG_KVM is not set 表示不编译 KVM 宿主机功能——我们的迷你 Linux 不需要在里面再开虚拟机。
编译
make -j$(nproc) bzImage32 核并行编译,83 秒完成。只编译 bzImage 不编译模块,因为 initramfs 方案不需要 .ko 文件。
Kernel: arch/x86/boot/bzImage is ready (#2)产物对比
| 产物 | allmodconfig | defconfig |
|---|---|---|
| bzImage | 40.7 MB | 14 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 加载到内存后,执行流程:
- 运行 Boot Sector 的 setup 代码,切换到保护模式
- 运行解压缩代码,将压缩的内核解压到内存
- 跳转到解压后的内核入口(
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。