2. 汽车嵌入式系统基础
说实话,很多搞嵌入式开发的朋友,一开始都习惯直接 apt install qemu-system-arm 完事。但做汽车电子调试,尤其是要对接硬件寄存器级的行为时,预编译的 QEMU 往往不够用。你得自己编译,才能打开那些调试开关,才能定制目标板。
这一章,我就带你走一遍完整的编译流程。从拉源码到跑起第一个 ARM 虚拟机,中间那些坑,我一个一个指给你看。
2.1 为什么非要自己编译?
预编译包和源码编译,差距其实挺大的。我列个表,你一看就明白:
| 对比项 | 预编译包(apt/pacman) | 源码编译 |
|---|---|---|
| 调试支持 | 通常不带 --enable-debug |
可开启 GDB 单步跟踪 QEMU 内部 |
| 目标架构 | 固定几个主流架构 | 可精确指定 ARMv7、RISC-V 64 等变体 |
| 性能优化 | 通用优化,不针对你的 CPU | 可加 -march=native 榨干性能 |
| 外设模型 | 只包含通用设备 | 可加入自定义板级支持包(BSP) |
我在做 ADAS 系统仿真时,就遇到过预编译 QEMU 不支持某款 Infineon 外设的情况。折腾了两天,最后自己编译加了个模块,十分钟搞定。所以,别嫌麻烦,这步省不了。
2.2 编译环境准备
先看看你的 Linux 发行版。我习惯用 Ubuntu 22.04 LTS,但 Debian、Fedora 也都行。关键是把依赖装全。
2.2.1 安装基础工具链
# Ubuntu / Debian
sudo apt update
sudo apt install git build-essential ninja-build \
pkg-config libglib2.0-dev libpixman-1-dev \
libslirp-dev libfdt-dev zlib1g-dev
# Fedora / RHEL
sudo dnf install git gcc gcc-c++ ninja-build \
pkgconfig glib2-devel pixman-devel \
libslirp-devel libfdt-devel zlib-devel
这里有个小细节:libslirp 是用户态网络栈,如果你不做网络仿真,可以不加。但我建议装上,因为后面调试网络协议栈时很有用。
sudo apt build-dep qemu-system-arm,它能自动拉满所有编译依赖,省得你一个一个查。
2.3 获取源码与选择版本
QEMU 的源码托管在 GitLab 上。我个人建议用稳定分支,别追 master,除非你想当小白鼠。
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
git checkout stable-8.2 # 或者 v8.2.0 标签
为什么选 8.2?因为它在 ARMv8.4 和 RISC-V 向量扩展上支持得比较全,汽车电子里常用的 Cortex-A76、RISC-V 64GC 都能跑。
2.4 配置目标架构
配置这一步,是编译的核心。说白了,就是告诉 QEMU:「我要编译哪些架构的模拟器,要不要调试功能,优化到什么程度」。
2.4.1 最小化配置(ARM 单架构)
mkdir build && cd build
../configure --target-list=arm-softmmu,aarch64-softmmu \
--enable-debug \
--enable-slirp \
--enable-kvm
--target-list:只编译 ARM 和 AArch64 的完整系统模拟器,省时间。--enable-debug:开启 QEMU 自身的调试符号,方便后续故障定位。--enable-kvm:如果宿主机支持 KVM,可以加速。
2.4.2 加入 RISC-V 支持
../configure --target-list=arm-softmmu,aarch64-softmmu,riscv32-softmmu,riscv64-softmmu \
--enable-debug \
--enable-slirp
嗯,这里要注意:RISC-V 的 softmmu 模式是完整系统模拟,如果你只想跑用户态程序,可以用 riscv64-linux-user。但做汽车电子,我们通常需要模拟整个 SoC,所以选 softmmu。
--enable-fdt,结果设备树传递一直失败。后来才发现,QEMU 的 RISC-V 板级模型依赖 libfdt 来生成设备树。所以,libfdt-dev 一定要装,配置时也要确认它被检测到了。
2.5 编译与安装
配置没问题后,直接开编:
make -j$(nproc)
sudo make install
-j$(nproc) 是用所有 CPU 核心并行编译。我见过有人用 -j1 编译,等了四十分钟……其实没必要,现代机器开满核心就行。
安装后,验证一下:
qemu-system-aarch64 --version
qemu-system-riscv64 --version
如果能正常输出版本号,说明编译成功了。
2.6 常见编译错误排查
编译过程中,你十有八九会遇到报错。别慌,我把最常见的几种列出来,附上解决方案。
| 错误信息 | 原因 | 解决办法 |
|---|---|---|
ERROR: glib-2.56 gthread-2.0 is required |
glib 版本太低或未安装 | sudo apt install libglib2.0-dev |
ERROR: pixman >= 0.21.8 required |
pixman 缺失 | sudo apt install libpixman-1-dev |
ERROR: DTC (libfdt) not present |
设备树编译器未安装 | sudo apt install libfdt-dev |
ninja: build stopped: subcommand failed. |
编译过程中断,通常是内存不足 | 减少并行数:make -j2 或增加 swap |
../meson.build:1:0: ERROR: Unknown compiler(s) |
缺少 C++ 编译器 | sudo apt install g++ |
2.6.1 一个真实案例
我记得有一次,在编译 RISC-V 的 spike 板级模型时,报了个 undefined reference to 'qemu_fdt_setprop'。查了半天,发现是 --enable-fdt 没生效,因为系统里装了 device-tree-compiler 但没装 libfdt-dev。这两个包不一样!前者是命令行工具,后者是开发库。装上 libfdt-dev 后重新 configure,问题解决。
2.7 知识结构总览
下面这张图,把整个编译流程串起来了。你可以把它当作一个检查清单:
2.8 验证编译结果
编译安装完成后,我建议你跑一个最小的 ARM 虚拟机,验证一下:
qemu-system-aarch64 -M virt -cpu cortex-a76 -smp 2 -m 1G \
-kernel /path/to/Image -initrd /path/to/initrd.img \
-append "console=ttyAMA0" -nographic
如果能正常启动内核,看到控制台输出,那恭喜你,编译环境完全 OK。
一个小技巧:如果你只是临时测试,可以用 -M none 加上 -device loader 来加载裸机程序,不需要完整的文件系统。这样验证速度更快。
好了,编译这关过了,后面就可以放心地做调试和故障定位了。记住,自己编译的 QEMU,就像自己改装的工具车,哪里不顺手就改哪里,这才是嵌入式工程师该有的态度。