4. QEMU 命令行基础:常用参数解析与设备配置

说实话,很多初学者拿到 QEMU 第一反应就是懵。命令行参数一长串,看着跟天书似的。我刚开始接触 QEMU 那会儿也是这样,对着文档翻半天,最后还是靠反复试错才摸清楚门道。

这一章我就把 QEMU 命令行里最常用的参数掰开揉碎讲清楚。你掌握了这些,后面搭建 RISC-V 仿真环境就会顺手很多。

4.1 QEMU 命令行基本结构

QEMU 的命令行格式其实很规整。说白了就是:

qemu-system-riscv64 [选项] [参数]

每个选项前面带一个短横线或双短横线。我习惯把参数分成几类:

  • 机器配置:-machine、-cpu、-smp
  • 内存配置:-m
  • 设备配置:-drive、-device、-netdev
  • 显示与交互:-nographic、-serial、-vga
  • 调试相关:-gdb、-S、-d

嗯,这里要注意:参数的顺序有时候会影响解析结果。我踩过这个坑,后面会细说。

4.2 -machine 与 -cpu 选项

这两个选项决定了你仿真的是哪块板子、哪颗芯片。它们是最基础的配置,也是最容易出错的地方。

-machine:选择目标板卡

QEMU 支持多种 RISC-V 板卡。你可以用 -machine help 查看当前版本支持哪些:

qemu-system-riscv64 -machine help

常见的 RISC-V 机器类型有:

机器名称 描述 适用场景
virt 通用虚拟平台 开发调试、裸机程序
sifive_u SiFive Unleashed 开发板 嵌入式 Linux 启动
spike_v1.10 Spike 模拟器兼容 RISC-V 指令集验证

我个人最常用的是 virt 机器。它没有真实硬件限制,设备配置灵活,非常适合做嵌入式调试。我在项目中遇到过用 sifive_u 跑 Linux 内核,结果因为设备树不匹配折腾了两天——后来换成 virt 就顺了。

小技巧: 用 -machine dumpdtb=myboard.dtb 可以导出当前机器的设备树文件。这个文件对调试驱动非常有用。

-cpu:选择处理器核心

RISC-V 的 CPU 配置比 ARM 灵活得多。你可以指定指令集扩展、特权级别、甚至自定义 CSR。

qemu-system-riscv64 -cpu help

常用 CPU 类型:

  • rv64:基础 64 位 RISC-V CPU
  • sifive-u54:SiFive U54 核心(支持 S 模式)
  • thead-c906:平头哥 C906 核心(支持向量扩展)

你还可以用 ? 查看某个 CPU 的详细特性:

qemu-system-riscv64 -cpu rv64,?  

这会列出所有可配置的特性,比如是否启用浮点单元、是否支持压缩指令等。我曾经调试一个裸机程序,发现浮点指令执行异常,最后查出来是 CPU 默认没开 F 扩展——加上 +f 就好了。

4.3 内存配置

内存配置用 -m 选项。语法很简单:

-m 256M        # 256MB 内存
-m 1G          # 1GB 内存
-m 512M,maxmem=2G  # 初始 512MB,最大可扩展到 2GB

你想想看,嵌入式系统里内存通常很紧张。我一般调试裸机程序用 128M 就够了,跑 Linux 内核至少 512M。如果要做图形界面或者跑大应用,建议 1G 起步。

注意: 内存大小会影响设备树中的内存节点。如果内存配置和实际硬件不一致,Linux 内核可能无法正确识别。我曾经因为 -m 256M 但设备树里写的是 512M,导致内核启动到一半就崩了。

4.4 设备配置

设备配置是 QEMU 最强大的部分。你可以给虚拟机挂载硬盘、网卡、串口、USB 等各种外设。

-drive:存储设备

最常用的就是挂载磁盘镜像:

-drive file=rootfs.img,format=raw,if=none,id=drive0
-device virtio-blk-device,drive=drive0

这里我解释一下:-drive 定义了一个后端存储,-device 把它挂到前端总线上。这种分离设计让 QEMU 非常灵活——你可以换不同的设备类型,后端存储文件不变。

常用的存储格式:

格式 说明 适用场景
raw 原始镜像,无压缩 性能优先
qcow2 QEMU 自有格式,支持快照 开发调试、测试
vmdk VMware 兼容格式 跨平台迁移

我个人习惯用 qcow2 格式做开发。它支持写时复制,可以快速创建多个测试环境,而且占用的实际磁盘空间比 raw 小得多。

-netdev:网络设备

网络配置稍微复杂一点。最简单的用法是用户模式网络:

-netdev user,id=net0
-device virtio-net-device,netdev=net0

这样虚拟机就能通过宿主机的网络访问外网了。但要注意,用户模式网络下,宿主机不能主动连接虚拟机。如果需要双向通信,得用 tap 模式:

-netdev tap,id=net0,ifname=tap0
-device virtio-net-device,netdev=net0

嗯,tap 模式需要宿主机有 root 权限,而且得提前创建好 tap 接口。我在项目中调试网络协议栈时,就用的 tap 模式,配合 Wireshark 抓包,非常方便。

-serial:串口配置

嵌入式开发离不开串口。QEMU 默认会创建一个串口,映射到标准输出:

-nographic          # 关闭图形界面,串口输出到终端
-serial mon:stdio   # 串口和 QEMU monitor 共享终端

如果你需要多个串口,可以这样:

-serial stdio       # 第一个串口
-serial tcp::4444,server,nowait  # 第二个串口映射到 TCP 端口

我曾经调试一个双串口设备,一个串口打印日志,另一个串口做交互控制。用 QEMU 的 TCP 映射,可以在宿主机上开两个终端窗口分别连接,调试效率高很多。

4.5 完整命令行示例

把上面这些参数组合起来,一个典型的 RISC-V 仿真启动命令长这样:

qemu-system-riscv64 \
  -machine virt \
  -cpu rv64 \
  -m 512M \
  -smp 4 \
  -nographic \
  -kernel vmlinux \
  -drive file=rootfs.img,format=raw,if=none,id=drive0 \
  -device virtio-blk-device,drive=drive0 \
  -netdev user,id=net0 \
  -device virtio-net-device,netdev=net0 \
  -append "root=/dev/vda console=ttyS0"

这个命令启动了一个 4 核、512MB 内存的 RISC-V 虚拟机,挂载了内核镜像和根文件系统,还配了网络。你直接复制过去,改一下文件路径就能用。

4.6 知识结构图

下面这张图把 QEMU 命令行的核心参数关系梳理清楚了:

QEMU 命令行参数结构图 QEMU 命令行 机器配置 内存配置 设备配置 显示与交互 -machine -cpu -smp -m (内存大小) -drive -device -netdev -nographic -serial 核心思路:先选机器和 CPU,再配内存,最后挂设备 参数顺序影响解析结果,建议按机器→CPU→内存→设备的顺序写

4.7 避坑指南

最后分享几个我踩过的坑:

参数顺序坑: QEMU 解析参数时,-device 引用的 id 必须在前面定义。比如 -device virtio-blk-device,drive=drive0 必须在 -drive ... id=drive0 之后。我刚开始写命令时经常搞反,结果 QEMU 报错说找不到设备。
调试技巧: 如果 QEMU 启动失败,先加 -d in_asm 看指令执行日志,或者加 -d guest_errors 看设备错误。这两个选项能帮你快速定位问题。

嗯,命令行基础就讲到这里。你把这些参数练熟了,后面搭建 RISC-V 仿真环境就会得心应手。记住,多试、多查 -help,QEMU 的文档虽然啰嗦,但确实管用。