4. QEMU启动内核基础:QEMU命令行参数详解与启动第一个内核镜像
QEMU(Quick Emulator)是嵌入式Linux内核调试中最核心的虚拟化工具。本章将系统讲解QEMU启动内核的关键命令行参数,并通过一个完整的实践案例,引导你成功启动第一个内核镜像。掌握这些基础参数,是后续进行内核异常定位和调试的前提。
4.1 QEMU启动内核的核心参数全景
QEMU模拟一台完整的计算机,启动内核的过程类似于物理机加电启动。以下五个参数构成了最常用的启动组合,它们分别对应内核镜像、初始根文件系统、内核启动参数、内存大小和CPU数量。
4.2 五大核心参数详解
4.2.1 -kernel:指定内核镜像
-kernel 参数用于指定QEMU加载的内核镜像文件。该镜像通常是经过编译的压缩内核,常见格式包括 bzImage(x86架构)和 zImage(ARM架构)。QEMU会模拟BIOS/UEFI启动过程,直接将内核加载到内存中并跳转到入口点执行。
qemu-system-x86_64 -kernel /path/to/bzImage
如果内核镜像路径包含空格或特殊字符,请使用双引号包裹。
4.2.2 -initrd:初始RAM磁盘
-initrd(Initial RAM Disk)参数指定一个压缩的根文件系统镜像(如 initrd.img 或 initramfs)。内核在启动初期会将该镜像加载到内存中,作为临时的根文件系统。initrd中通常包含必要的设备驱动、模块和初始化脚本,用于挂载真正的根文件系统。
| 特性 | initrd (ramdisk) | initramfs (cpio) |
|---|---|---|
| 文件系统类型 | 基于块设备的ramdisk | 基于tmpfs的cpio归档 |
| 大小限制 | 固定大小,需预先分配 | 动态增长,按需分配 |
| 加载方式 | 内核解压到ramdisk块设备 | 内核直接解压到tmpfs |
| 推荐使用 | 旧内核或特殊场景 | 现代Linux发行版标准方式 |
CONFIG_BLK_DEV_INITRD 选项。QEMU对两种格式均支持,但推荐使用initramfs以获得更好的灵活性和性能。
4.2.3 -append:内核启动参数
-append 参数用于向内核传递命令行参数,这些参数与物理机上通过GRUB传递的参数作用完全相同。常见参数包括:
- console:指定控制台设备,如
console=ttyS0将串口作为控制台。 - root:指定根文件系统设备,如
root=/dev/sda1或root=/dev/nfs。 - init:指定第一个用户空间进程,默认为
/sbin/init。 - quiet:减少内核启动时的日志输出。
- debug:开启内核调试信息输出。
qemu-system-x86_64 -kernel bzImage -initrd initrd.img \
-append "console=ttyS0 root=/dev/ram0 rdinit=/sbin/init"
多个参数之间用空格分隔,整个参数字符串需要用双引号包裹。
4.2.4 -m:内存大小
-m 参数用于指定QEMU虚拟机可用的物理内存大小。默认单位为MB,也可以使用 G 后缀指定GB。合理的内存分配对内核启动和后续调试至关重要。
| 场景 | 推荐内存大小 | 说明 |
|---|---|---|
| 最小启动测试 | 64M ~ 128M | 仅启动内核和initramfs,无图形界面 |
| 标准嵌入式调试 | 256M ~ 512M | 可运行Busybox或小型Linux系统 |
| 完整发行版调试 | 1G ~ 4G | 运行Ubuntu/Debian等完整系统 |
| 内核内存调试 | 512M ~ 2G | 开启KASAN/SLUB调试时需更多内存 |
4.2.5 -smp:对称多处理
-smp 参数用于指定QEMU模拟的CPU核心数量。对于单核调试,使用 -smp 1 可以简化问题;对于多核并发问题,可以设置 -smp 4 或更多。
# 单核模式(推荐用于调试)
qemu-system-x86_64 -kernel bzImage -smp 1
# 四核模式(测试多核调度)
qemu-system-x86_64 -kernel bzImage -smp 4
# 指定拓扑结构
qemu-system-x86_64 -kernel bzImage -smp 4,sockets=1,cores=4,threads=1
4.3 启动第一个内核镜像:完整实践
下面通过一个完整的实践案例,展示如何使用QEMU启动一个最小Linux内核。我们将使用Busybox构建的最小根文件系统作为initramfs。
4.3.1 准备内核镜像和initramfs
假设你已经编译好了一个x86_64架构的内核镜像 bzImage,并准备好了initramfs文件 initramfs.cpio.gz。如果还没有,可以使用以下命令快速生成一个最小initramfs:
# 创建一个最小initramfs(使用Busybox)
mkdir -p initramfs/{bin,sbin,etc,proc,sys,dev}
cp /path/to/busybox initramfs/bin/
# 创建必要的设备节点
sudo mknod initramfs/dev/console c 5 1
sudo mknod initramfs/dev/null c 1 3
# 创建init脚本
cat > initramfs/init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Hello from QEMU Linux!"
exec /bin/sh
EOF
chmod +x initramfs/init
# 打包为cpio.gz
cd initramfs
find . | cpio -H newc -o | gzip > ../initramfs.cpio.gz
cd ..
4.3.2 编写QEMU启动命令
将以下命令保存为 boot.sh 脚本:
#!/bin/bash
# QEMU启动第一个内核镜像
qemu-system-x86_64 \
-kernel ./bzImage \
-initrd ./initramfs.cpio.gz \
-append "console=ttyS0 rdinit=/init" \
-m 256M \
-smp 1 \
-nographic
-nographic:不使用图形窗口,所有输出重定向到当前终端,便于调试。console=ttyS0:将串口作为控制台,与-nographic配合使用。rdinit=/init:指定initramfs中的init程序路径。
4.3.3 执行启动并观察输出
运行启动脚本:
chmod +x boot.sh
./boot.sh
你将看到内核启动日志从终端输出,最终进入Busybox的shell提示符。如果一切正常,输出应包含类似以下内容:
...
[ 0.000000] Linux version 6.1.0 ...
[ 0.123456] console [ttyS0] enabled
...
Hello from QEMU Linux!
/ # _
4.4 常见问题与排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| QEMU提示 "Could not open kernel image" | 内核镜像路径错误或文件不存在 | 检查 -kernel 参数路径是否正确,使用绝对路径 |
| 内核启动后卡住,无任何输出 | console参数未正确设置 | 添加 console=ttyS0 并配合 -nographic |
| 提示 "Kernel panic - not syncing: VFS: Unable to mount root fs" | initramfs未正确加载或根文件系统损坏 | 检查 -initrd 路径,确认cpio.gz文件完整 |
| 内存不足导致启动失败 | -m 分配的内存过小 | 增加内存分配,建议至少256M |
| 多核启动异常 | ACPI或SMP配置问题 | 先使用 -smp 1 排除多核问题 |
-nographic 模式,并将内核日志级别设置为 loglevel=8(通过 -append 传递),以获取最完整的启动日志。日志是定位启动异常的第一手资料。
4.5 本章小结
本章详细讲解了QEMU启动内核的五个核心参数:-kernel 指定内核镜像、-initrd 提供初始根文件系统、-append 传递内核启动参数、-m 控制内存大小、-smp 设置CPU数量。通过完整的实践案例,你已经成功启动了第一个内核镜像。这些基础操作是后续进行内核异常定位和QEMU调试的基石,请务必熟练掌握。