2. QEMU系统模式基础:QEMU架构简介、系统模式与用户模式区别、支持的硬件架构
好,咱们今天聊聊QEMU的系统模式。说实话,我刚接触QEMU那会儿,也被它那堆模式搞得有点晕。系统模式、用户模式,到底有啥区别?别急,咱们一点点拆开看。
2.1 QEMU的整体架构
QEMU本质上是个虚拟机监控器,或者说是个硬件模拟器。它的核心思想很简单:用软件模拟硬件。你想想看,你的电脑是x86架构,但你想跑一个ARM的程序,怎么办?QEMU就是干这个的。
它的架构分两层:
- 前端:负责解析客户机的指令,把它翻译成中间代码(TCG,Tiny Code Generator)
- 后端:把中间代码翻译成宿主机能执行的指令
说白了,就是动态二进制翻译。QEMU把客户机的每一条指令,实时翻译成宿主机指令。这个过程是动态的,不是提前编译好的。我在项目中遇到过一个问题:客户机跑着跑着突然卡死,查了半天才发现是TCG翻译缓存满了,需要清空重来。嗯,这个坑后面会细说。
核心概念:QEMU不是硬件虚拟化(像VMware那样),它是纯软件模拟。所以它慢,但灵活。你可以在x86上模拟ARM、MIPS、RISC-V,甚至模拟一个完整的开发板。
我习惯把QEMU的架构画成下面这张图,你一看就明白了:
2.2 系统模式 vs 用户模式
这两个模式,我刚开始也搞混过。简单说:
- 系统模式(System Mode):模拟整个计算机系统,包括CPU、内存、外设。你可以在这个模式里跑一个完整的操作系统。
- 用户模式(User Mode):只模拟CPU指令集,不模拟外设。只能跑单个应用程序,不能跑操作系统。
为什么会这样?因为用户模式直接利用宿主机的系统调用。你想想看,客户机程序要读写文件,用户模式就直接调用宿主机的open()、read()。省事,但功能有限。
我的建议:如果你要调试固件,比如一个RTOS或者Linux内核,必须用系统模式。用户模式只适合跑一些简单的测试程序。
我曾经踩过一个坑:用用户模式跑一个带中断处理的程序,结果发现中断根本触发不了。查了半天文档才明白——用户模式不模拟中断控制器。嗯,这个教训挺深刻的。
两者的区别,我整理了一张表:
| 特性 | 系统模式 | 用户模式 |
|---|---|---|
| 模拟范围 | 完整硬件(CPU + 外设 + 内存) | 仅CPU指令集 |
| 能否跑OS | 可以(Linux、RTOS等) | 不可以 |
| 启动方式 | qemu-system-arm ... |
qemu-arm ... |
| 性能 | 较慢(模拟全部硬件) | 较快(直接利用Host系统调用) |
| 调试能力 | 强(可调试内核、驱动、中断) | 弱(只能调试用户态程序) |
| 典型用途 | 固件开发、内核调试、系统仿真 | 交叉编译测试、快速验证 |
2.3 支持的硬件架构
QEMU支持的架构非常多,咱们重点说三个:ARM、MIPS、RISC-V。这三个在嵌入式领域最常用。
2.3.1 ARM
ARM是QEMU支持最完善的架构。我个人习惯用qemu-system-arm来模拟各种ARM开发板,比如:
- versatilepb:经典的ARM926EJ-S,适合入门
- vexpress-a9:Cortex-A9,支持多核
- raspi2/raspi3:树莓派2/3,适合跑Linux
- stm32-p103:Cortex-M3,适合裸机开发
我在项目中用过vexpress-a9来调试一个Linux内核模块。说实话,比在真机上调试方便多了——可以随时打断点、看寄存器、甚至单步执行内核代码。
2.3.2 MIPS
MIPS在QEMU里用qemu-system-mips。常见的模拟平台有:
- malta:MIPS Malta开发板,支持32位和64位
- fulong2e:龙芯2E,虽然龙芯现在走自己的路,但MIPS兼容性还在
我记得有一次帮客户调试一个MIPS路由器固件,客户说硬件还没回来,让我先用QEMU顶着。我用malta板模拟了MIPS32架构,把固件跑起来了。虽然有些外设模拟得不够精确,但核心逻辑都验证通过了。
注意:MIPS的字节序有点特殊。MIPS有大端(BE)和小端(LE)两种模式。启动QEMU时一定要指定正确的字节序,否则程序跑起来全是乱码。我曾经因为这个折腾了一下午。
2.3.3 RISC-V
RISC-V是后起之秀,QEMU的支持也越来越好。用qemu-system-riscv32或qemu-system-riscv64。常见的模拟平台:
- virt:通用虚拟平台,支持各种RISC-V特性
- sifive_u:SiFive Unleashed开发板,U54核心
- spike:Spike模拟器兼容模式
我个人觉得RISC-V在QEMU上的体验是最好的。为什么?因为RISC-V本身设计简洁,QEMU模拟起来也干净。没有ARM那么多历史包袱,也没有MIPS那些奇怪的字节序问题。
下面这张表总结了三个架构的QEMU命令:
| 架构 | QEMU命令 | 常用模拟平台 | 典型应用 |
|---|---|---|---|
| ARM | qemu-system-arm |
vexpress-a9, raspi3, stm32-p103 | Linux内核、RTOS、裸机程序 |
| MIPS | qemu-system-mips |
malta, fulong2e | 路由器固件、嵌入式Linux |
| RISC-V | qemu-system-riscv64 |
virt, sifive_u | RISC-V开发、教学、验证 |
2.4 一个简单的系统模式启动示例
光说不练假把式。咱们来个实际的例子,用系统模式启动一个ARM Linux内核:
# 下载内核和设备树
wget https://example.com/zImage
wget https://example.com/vexpress-v2p-ca9.dtb
# 启动QEMU系统模式
qemu-system-arm \
-M vexpress-a9 \
-kernel zImage \
-dtb vexpress-v2p-ca9.dtb \
-append "console=ttyAMA0" \
-nographic \
-m 512M
这个命令的意思是:模拟vexpress-a9开发板,加载内核zImage,设备树文件,指定串口输出,不使用图形界面,分配512MB内存。
启动后你会看到Linux内核的启动日志,最后进入一个shell。嗯,这时候你就可以像操作一台真实的ARM开发板一样,执行各种命令了。
小技巧:加上-s参数可以开启GDB调试端口(默认1234)。这样你就可以在宿主机上用GDB远程调试内核了。我几乎每次用QEMU都会加上这个参数。
好了,关于QEMU系统模式的基础就聊这么多。记住一句话:系统模式模拟整个硬件,用户模式只模拟CPU。搞清楚了这一点,后面的内容就好理解了。
公众号:蓝海资料掘金营,微信deep3321