搞 ARM 交叉编译、内核模块加载、UEFI 启动流程验证这类活儿的时候,一台 ARM64 虚拟机真的是刚需。我之前的做法是去云上开 ARM 实例,但有些场景需要在本地反复冷启动、在内核里加断点、还要刷 UEFI 变量,这时候在 x86 宿主机上用 libvirt 和 QEMU 模拟一台 ARM64 机器就非常合适。这篇博客就完整记录我在 CentOS 8 上把整套环境搭起来的过程,包括 AAVMF 固件配置、virt-install 参数、串口 console 黑屏这类坑点,工具链和排查思路都会讲清楚,给同样被 ARM64 模拟折腾过的朋友一个能直接抄的作业。
1. 环境准备与总体思路
1.1 宿主机硬件与系统要求
先盘点一下硬件条件。QEMU 的 ARM64 系统级模拟会同时模拟 CPU、内存、串口、磁盘控制器、网卡和中断控制器,CPU 占用率通常不低,所以宿主机最好满足两个条件:CPU 支持虚拟化扩展(Intel VT-x 或 AMD-V),内存至少 16GB,磁盘剩余空间至少 30GB。虚拟化扩展在纯模拟场景下其实用不到,因为 x86 上跑 ARM64 只能靠 TCG(Tiny Code Generator)动态翻译指令,KVM 并不能帮忙加速;但如果你之后还想在同机跑 x86 KVM 虚拟机,这个扩展就是刚需。
系统方面,标题写的是 CentOS 8,但我必须提醒一句:CentOS 8 官方仓库早已进入 EOL 状态,正常dnf install可能会因为源不可用而失败。实际搭建时建议用 CentOS Stream 8,或者换成 Rocky Linux 8 / AlmaLinux 8,本文的命令在这几个发行版上基本通用。如果只有 CentOS 8 的机器,记得把BaseOS、AppStream、PowerTools等仓库切换到可用的镜像源,否则装包会卡在下载阶段。
这篇文章面向的读者,是那些已经有基础 Linux 操作经验、但没怎么碰过 ARM64 模拟的工程师。阅读前不用懂 QEMU 源码,只需要会敲常用命令、能看懂 XML 就行。整个搭建过程大约需要 30 到 40 分钟,大部分时间花在下载镜像和安装系统上。
1.2 为什么要在 x86 宿主机上模拟 ARM64
我最早接触 ARM64 虚拟机,是因为要验证一段只能在aarch64架构下编译的内核模块。公司没有现成的 ARM 开发板,用云服务的话每次都要重新配环境、传文件,调试内核时还不能随便重启,否则丢失现场。后来发现 QEMU 的 ARM64 系统模拟已经足够成熟,直接在笔记本上开一台虚拟机,既能跑完整的 Linux 发行版,又能方便地打快照、改配置、随时销毁重建。
这个方案相比物理 ARM 设备有几个明显优势:第一,虚拟机镜像可以版本化管理,一个qcow2文件复制走就是完整环境,不怕硬件损坏;第二,宿主机上的日志、断点、GDB 调试可以直接对接客户机,本地文件系统也能通过 virtiofs 共享;第三,快照可以随意做,内核编坏了回滚即可,不需要重新刷机。缺点是性能没办法和真实 ARM 硬件比,尤其是在 x86 上做指令翻译,CPU 密集型任务可能只有原生性能的十分之一。
所以我的建议是:如果只是偶尔跑一下交叉编译、测试启动流程、验证 GRUB 和 UEFI 交互,这篇文章的方案完全够用;如果要持续做高负载 ARM CI,还是老老实实用云上的 ARM 实例或工控机。
1.3 方案选型:QEMU 系统模拟 + libvirt 管理
在 x86 宿主机上跑 ARM64 虚拟机,核心工具就是 QEMU 的qemu-system-aarch64,但我不建议直接手写命令行启动。主要原因有二:一是 QEMU 参数非常长,包含固件、机器类型、CPU 模型、串口、磁盘、网络等十几项,手敲容易漏;二是后期要管理快照、迁移、克隆,手写命令的方式很难维护。
更好的做法是让 libvirt 统一管理,需求描述成 XML 文件,底层调用 QEMU 完成虚拟机生命周期管理。libvirt 会自动计算 QEMU 参数、管理 UEFI 变量存储、配置网络,还能通过virsh、virt-manager、Cockpit 等工具操作虚拟机。虽然手动跑 QEMU 在某些调试场景下更灵活,比如想看完整 QEMU 日志时可以直接-d参数,但日常建机和维护用 libvirt 绝对是效率最高的。
另外要提醒一个容易混淆的概念:QEMU 有两种模拟方式,一种是同架构下的硬件加速(KVM),另一种是跨架构的动态翻译(TCG)。本文场景是 x86 宿主机模拟 ARM64 客户机,属于跨架构,KVM 无法参与,只能走 TCG。这一点决定了性能上限,也是后面很多“坑”的根源。
2. 安装与配置 libvirt、QEMU/KVM
2.1 安装软件包
在 CentOS 8 或 Rocky 8 上,安装命令如下:
dnf install -y qemu-kvm libvirt virt-install edk2-aarch64这几个包的角色各不相同:qemu-kvm提供 QEMU 主体程序,其中包含qemu-system-aarch64,这是模拟 ARM64 机器的核心进程;libvirt提供虚拟化管理 API 和libvirtd守护进程;virt-install是命令行建机工具,会把我们的需求转换成 QEMU 参数;edk2-aarch64是 AArch64 平台的 UEFI 固件,也就是 AAVMF,没有它在启动 ARM64 虚拟机时会找不到引导固件。
有的教程还会让你额外安装qemu-system-aarch64这个独立包,但实际上 CentOS 8 的qemu-kvm已经带了对应二进制,没必要重复装。如果你在dnf install时看到依赖冲突,优先确认是否启用了 EPEL 或 AppStream 里多余的 QEMU 模块。用dnf module list qemu可以查看模块状态,如果存在多个 QEMU 版本模块,需要先执行dnf module reset qemu再安装。
装完以后,用qemu-system-aarch64 --version验证一下,能输出版本号就说明二进制没问题。
2.2 启用并验证 libvirtd
安装完成后,需要启动并设置开机自启:
systemctl enable --now libvirtd systemctl status libvirtd这时用virsh list --all应该能看到虚拟机列表,当前是空的。还要确认默认网络存在并处于活动状态:
virsh net-list --all virsh net-start default virsh net-autostart default默认网络是 NAT 模式,虚拟机通过它访问外部网络。如果你漏掉net-start default,后面虚拟机起来后可能没有网络,这正是常见坑点之一。建议直接设置net-autostart default,省得每次宿主机重启后手动激活。
2.3 验证 QEMU 对 AArch64 的支持
输入下面的命令,检查 QEMU 是否真的支持 ARM64 系统模拟:
qemu-system-aarch64 -M help qemu-system-aarch64 -cpu help-M help会列出所有可用的机器类型,我们要用的是virt,这是一个专为 QEMU 设计的高层虚拟平台,模拟了 PCIe、GIC 中断控制器、串口等设备,适合跑标准发行版。-cpu help会列出支持的 CPU 模型,常见的有cortex-a57、cortex-a53、cortex-a72、max等。选择max可以获得最完整的特性集,但实际模拟中我用cortex-a57,兼容性和稳定性都更好。
这里顺便回答一个高频疑问:ARM64 和 AMD64(x86_64)到底有何不同。AMD64 是 Intel/AMD 主导的 CISC 架构,指令定长编码、生态成熟;ARM64 是精简指令集,指令定长 32 位,能耗比高。二者二进制不兼容,所以内核、驱动、应用都必须针对目标架构单独编译。QEMU 的 TCG 动态翻译,就是要把 ARM64 的指令逐块翻译成 x86 指令,这也是性能开销大的核心原因。
3. 准备 ARM64 系统镜像与 UEFI 固件
3.1 镜像选择与下载
ARM64 虚拟机需要 ARM64 版本的安装介质。CentOS 8 官方已经停止维护,建议用 CentOS Stream 8 或 Rocky Linux 8 的 ARM64 ISO。如果只是为了跑通用环境,Ubuntu Server ARM64 也可以,但本文的配置命令以 RHEL 系为主,所以我推荐 Rocky Linux 8。下载时认准架构标识,通常文件名里会带aarch64或arm64,别下载成 x86_64。
ISO 镜像的下载地址可以去发行版官网的镜像列表里找,用wget或curl拉下来即可,例如:
wget https://download.rockylinux.org/pub/rocky/8/isos/aarch64/Rocky-8.10-aarch64-minimal.iso如果网络环境不够稳定,也可以下载云镜像(qcow2 格式),后期配合 cloud-init 直接注入用户和 SSH 密钥,相对 ISO 安装更省事。不过 ISO 安装能看到完整的安装器交互,适合第一次搭环境时理解 UEFI 启动流程。文章后面我也会给云镜像的适配思路,两条路线都能走通。
我习惯把镜像和磁盘分别放在两个目录,例如/data/iso和/data/vms,避免根目录被大文件塞满。ARM64 的 ISO 通常比 x86 版本略小,但也接近 1GB,磁盘空间要留足。
3.2 获取 AAVMF 固件
QEMU 的 ARM64virt机器默认没有固件,它不像 x86 的 SeaBIOS 那样自动加载,必须显式指定 UEFI 固件文件。这也是新手最容易卡住的地方。在 RHEL 系发行版上,固件包就是前面装的edk2-aarch64,安装后文件分布在:
ls -l /usr/share/AAVMF/正常情况下会看到AAVMF_CODE.fd、AAVMF_VARS.fd、AAVMF_CODE.verbose.fd等文件。CODE.fd是只读的 UEFI 固件主体,VARS.fd是 UEFI 变量存储,专门保存引导项、启动顺序、Secure Boot 状态等信息。
使用上有一个非常重要的原则:每个虚拟机必须拥有自己独立的VARS.fd副本,不能多个虚拟机共用同一个文件,否则 UEFI 变量会互相覆盖,导致启动项错乱。libvirt 在创建虚拟机时会自动从模板复制一份到/var/lib/libvirt/qemu/nvram/,但如果你手动指定固件,这一点必须自己注意。后面我给的 XML 配置里会体现这个逻辑。
3.3 创建虚拟机磁盘
先用qemu-img创建一块 qcow2 格式的磁盘:
qemu-img create -f qcow2 /data/vms/arm64-rocky.qcow2 30Gqcow2是 QEMU 的默认磁盘格式,按需增长,支持快照,压缩和加密也能做。30G是虚拟磁盘的最大容量,不是立刻占用,所以给大一点没有心理负担。如果你下载的是官方云镜像,可以直接用qemu-img convert把原始格式转换成 qcow2,并顺便调整容量:
qemu-img convert -f qcow2 -O qcow2 Rocky-8-aarch64.qcow2 /data/vms/arm64-rocky.qcow2 qemu-img resize /data/vms/arm64-rocky.qcow2 30G如果后期需要 UEFI 引导且分区表有问题,可以再用virt-resize或growpart调整分区,但第一步先把磁盘文件准备好。
4. 使用 libvirt 定义 ARM64 虚拟机
4.1 最简 virt-install 建机命令
下面这条命令是我实际跑通过的,可以直接复制修改:
virt-install \ --name arm64-rocky \ --arch aarch64 \ --machine virt \ --vcpus 4 \ --memory 8192 \ --cpu cortex-a57 \ --os-variant rhel8.0 \ --disk path=/data/vms/arm64-rocky.qcow2,size=30,bus=virtio \ --cdrom /data/iso/Rocky-8.10-aarch64-minimal.iso \ --network network=default,model=virtio \ --graphics none \ --serial pty \ --console pty,target_type=serial \ --boot uefi拆开看几个关键参数:
--arch aarch64告诉 libvirt 这是一台 ARM64 机器,只有设了它,libvirt 才会调用qemu-system-aarch64。--machine virt对应前面讲的 QEMU machine type,必须指定virt,这是 ARM64 模拟的通用平台。--cpu cortex-a57指定模拟的 CPU 型号,用cortex-a57比默认模型更可控。--graphics none是因为我们走串口控制台,不需要 VNC 图形界面;--serial pty和--console pty,target_type=serial让 libvirt 把客户机串口接到 pty,后面可以用virsh console登录。--boot uefi是关键中的关键,libvirt 会自动寻找 AAVMF 固件。如果找不到,就会报错提示缺少 AArch64 UEFI 固件,这就是我在前面强调要装edk2-aarch64的原因。
如果你的virt-install版本较老(CentOS 8 默认的版本可能不完全支持--boot uefi自动查找 AArch64 固件),可以用手动指定固件的方式:
virt-install \ --name arm64-rocky \ --arch aarch64 \ --machine virt \ --vcpus 4 \ --memory 8192 \ --cpu cortex-a57 \ --os-variant rhel8.0 \ --disk path=/data/vms/arm64-rocky.qcow2,bus=virtio \ --cdrom /data/iso/Rocky-8.10-aarch64-minimal.iso \ --network network=default,model=virtio \ --graphics none \ --serial pty \ --console pty,target_type=serial \ --boot loader=/usr/share/AAVMF/AAVMF_CODE.fd,loader_ro=yes,nvram_template=/usr/share/AAVMF/AAVMF_VARS.fd,loader_type=pflash4.2 生成的 XML 配置解析
执行virsh dumpxml arm64-rocky可以看到完整 XML。核心部分的含义值得逐行搞清楚,因为以后调参数、排查问题都依赖对这些字段的理解。
<domain type='qemu'> <name>arm64-rocky</name> <memory unit='KiB'>8388608</memory> <vcpu placement='static'>4</vcpu> <os> <type arch='aarch64' machine='virt'>hvm</type> <loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader> <nvram template='/usr/share/AAVMF/AAVMF_VARS.fd'>/var/lib/libvirt/qemu/nvram/arm64-rocky_VARS.fd</nvram> </os> <cpu mode='custom' match='exact'> <model fallback='allow'>cortex-a57</model> </cpu> <devices> <controller type='pci' index='0' model='pcie-root'/> <interface type='network'> <source network='default'/> <model type='virtio'/> </interface> <serial type='pty'> <target type='system-serial' port='0'> <model name='pl011'/> </target> </serial> <console type='pty'> <target type='serial' port='0'/> </console> </devices> </domain>这里最值得关注的是<os>部分。loader指定 UEFI 固件 CODE 文件并设为只读;nvram指定这台虚拟机自己的 UEFI 变量副本,template是初始模板。libvirt 会自动复制一份模板到/var/lib/libvirt/qemu/nvram/arm64-rocky_VARS.fd,这样每台虚拟机都有独立的引导变量空间。
<serial>的model默认就是pl011,这是 ARM64 virt 平台的串口控制器,对应客户机里的串口设备是/dev/ttyAMA0。很多人在安装 ARM64 虚拟机时卡在黑屏,就是因为串口参数没配对,这个我后面专门讲。
4.3 启动虚拟机并进入安装界面
接下来就可以启动虚拟机了:
virsh start arm64-rocky virsh console arm64-rockyvirsh console会进入客户机的串口控制台。此时应该能看到 UEFI 引导画面或安装器输出。如果屏幕完全没反应,先别急着反复重启,很可能是串口设备名或 GRUB 参数的问题,第六章会给出具体排查方法。
正常进入安装器后,按照惯例选择语言、时区、软件源。分区这里必须特别留意:ARM64 的 UEFI 引导要求磁盘是 GPT 分区表,同时需要一个 FAT 格式的 EFI 系统分区(挂载点/boot/efi)。我建议手动分区,不要用“自动创建分区”一路到底,因为自动分区有时会漏掉/boot/efi,导致安装完成后无法引导。
5. 系统安装与 UEFI 引导配置细节
5.1 分区与引导流程
安装器里选择自定义分区,创建如下布局:
/boot/efi:类型 EFI System Partition,大小 200MB 到 512MB,文件系统 vfat。/boot:建议 1GB,文件系统 ext4 或 xfs。/:剩余空间,文件系统 ext4 或 xfs。
安装引导程序时,目标选择这块虚拟磁盘即可。RHEL 系安装器会自动调用grub2-install把 GRUB 安装到 EFI 系统分区,并在 UEFI 变量里注册引导项。整个过程和 x86 的 UEFI 安装很像,区别只在于 GRUB 的二进制架构是aarch64-efi。
安装完成并重启后,正常应该先进入 UEFI 固件,然后根据引导项加载 GRUB,最后进入系统。如果重启后卡在 UEFI Shell 或者提示找不到引导项,多半是VARS.fd里的引导变量没有写进去,或者 EFI 分区里的/EFI/BOOT/BOOTAA64.EFI缺失,这个问题第六章会重点排查。
5.2 UEFI 变量与引导项调整
登录系统后,可以用efibootmgr查看和管理 UEFI 引导项:
efibootmgr -v输出里会列出BootCurrent、BootOrder和具体的Boot0000等引导项。如果引导项丢失,可以手动添加:
efibootmgr -c -d /dev/vda -p 1 -L "Rocky Linux" -l \\EFI\\rocky\\shimaa64.efi注意路径中反斜杠的写法,这是 UEFI 固件的路径格式。如果 GRUB 损坏,可以进入急救模式后用chroot到已安装系统,重新执行grub2-install和grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg。这些都是常规操作,但在 ARM64 模拟下执行时会因为设备名、架构路径不同而容易出错,务必确认/boot/efi已经挂载。
5.3 串口 console 参数与黑屏问题
ARM64 virt 机器默认不会自动开启串口登录,需要在 GRUB 内核命令行里加上串口参数,否则安装完登录时virsh console可能只看到黑屏。编辑/etc/default/grub,在GRUB_CMDLINE_LINUX中加入:
console=ttyAMA0,115200然后重新生成 GRUB 配置:
grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg这里的ttyAMA0对应 QEMU virt 平台的 PL011 串口,是 libvirt XML 里<model name='pl011'/>的设备。如果选用了virt机器但串口启动参数写成了ttyS0,就会出现控制台黑屏但系统其实已经启动的现象。这是一个非常典型的坑,很多入门 ARM64 模拟的人都会在这里卡一晚上。
6. 常见问题与排查技巧实录
6.1 启动即黑屏或virsh console无输出
这是 ARM64 模拟中最常见的现象,原因基本集中在两块:一是 UEFI 固件没加载对,二是串口参数不匹配。
排查顺序如下:
- 查看虚拟机运行状态:
virsh list --all,确认不是启动瞬间就崩了。 - 查看 QEMU 日志:
tail -f /var/log/libvirt/qemu/arm64-rocky.log,这里会记录 QEMU 的启动参数和报错信息,是最直接的诊断入口。 - 确认固件加载正常:如果日志里有
Could not open '/usr/share/AAVMF/AAVMF_CODE.fd',说明edk2-aarch64没装好,重装一次。 - 如果 UEFI 有输出但安装器无输出,检查内核命令行
console=是否包含ttyAMA0,并且 GRUB 配置文件是否真的更新了。
有时 UEFI 固件自己也会向串口输出,但量很少,容易误以为黑屏。可以试着用virsh sendkey发送回车键,观察是否有反应。在排障时我倾向于先用virt-install创建一台最小虚拟机,只接串口、不放磁盘,如果 UEFI 能正常输出,说明 QEMU 和固件链路没问题,再逐步加设备定位。
6.2 提示 “No bootable device” 或卡在 UEFI Shell
这个问题的本质是 UEFI 找不到可启动的 EFI 应用。常见原因有三个:磁盘上根本没有 EFI 系统分区,或者/EFI/BOOT/BOOTAA64.EFI不存在,或者 UEFI 引导变量里没有对应条目。
解决办法如下:
- 在 UEFI Shell 里输入
FS0:切换到 EFI 分区,执行ls看有没有EFI目录。 - 如果存在
/EFI/rocky/shimaa64.efi,尝试用bootaa64.efi或shimaa64.efi文件手动启动。 - 进入系统急救模式,检查
/boot/efi是否挂载,重新生成 GRUB 配置。 - 如果 VARS 文件被污染,可以直接删除这台虚拟机的
nvram文件,并在 XML 里重新指定nvram_template,然后重建引导项。
这个坑在云镜像转换场景尤其常见,很多云镜像默认不装 UEFI 引导,需要自己补 Grub2。
6.3 安装完 GRUB 失败或 UEFI 变量写不进去
RHEL 系安装器在 ARM64 上偶尔会报错“Failed to install bootloader”。这通常不是因为 GRUB 本身坏了,而是 UEFI 变量存储没有正确暴露给安装器。检查两点:
/sys/firmware/efi在客户机里是否存在且非空。如果没有这个目录,说明系统并不是以 UEFI 模式启动的,而是 BIOS/CSM 模式,ARM64 virt 一般不支持传统 BIOS,所以要回到第 4 章检查固件配置。- 确认
VARS.fd文件可写。libvirt 管理的 nvram 路径通常是/var/lib/libvirt/qemu/nvram/arm64-rocky_VARS.fd,如果这个文件只读或属主不对,安装器无法写入引导变量。
还有一种情况是虚拟机启动时 UEFI Secure Boot 打开,而安装介质/GRUB 没有正确签名,导致引导被拦截。低级问题就先在 XML 里关闭 Secure Boot,等系统装好后再研究。
6.4 虚拟机起不来,libvirt 报权限错误
模拟 ARM64 虚拟机时的权限问题,集中在几个目录:/data/vms下磁盘文件的属主不是qemu:qemu,导致 QEMU 进程没权限读;/var/lib/libvirt/qemu/nvram/需要libvirt-qemu用户写权限。解决办法:
chown qemu:qemu /data/vms/arm64-rocky.qcow2 chmod 640 /data/vms/arm64-rocky.qcow2如果使用 SELinux,还需要确认磁盘文件的 SELinux 标签正确,比如virt_image_t:
semanage fcontext -a -t virt_image_t "/data/vms(/.*)?" restorecon -Rv /data/vms6.5 网络不通或 SSH 连接不上
如果默认 NAT 网络没启动,虚拟机内部网卡会一直 down。排查从宿主开始:
virsh net-list --all ip a show virbr0正常情况下virbr0网桥应该存在并有 IP 地址。客户机内部用ip a看网卡是否有 IP,如果没有,可能是 DHCP 没有跑起来,手动执行dhclient试试。我遇到过一种情况是qemu-system-aarch64的 virtio-net 驱动没加载,导致客户机里看不到网卡,这种就需要在安装系统时确认内核里包含了 virtio_net 模块,或者改用model=e1000e轮询测试。
6.6 性能太差,能不能优化
TCG 模拟的性能瓶颈是天然存在的,但有一些缓解手段:
- 调整 vCPU 数量。基础实验用
--vcpus 2反而比--vcpus 8更稳,因为 TCG 的多线程调度开销在跨架构翻译时很明显。 - 不要盲目添加内存。内存过大导致宿主机 swap,反而拖慢整体性能,8GB 是模拟场景下的甜点值。
- 磁盘和网卡都用 virtio,减少模拟设备的中断开销。
- 编译大项目时,考虑把源码目录放到宿主机上通过 virtiofs 共享,避免在客户机里反复写磁盘。
- 如果只是编译 ARM64 用户态程序,其实不需要跑系统级模拟,直接装
qemu-user-static会更高效。系统级模拟适合测内核、驱动和完整发行版。
7. 性能优化与进阶玩法
7.1 使用 virtio 设备与多队列网卡
在 XML 中已经配置了bus='virtio'的磁盘和model='virtio'的网卡。virtio 是半虚拟化设备模型,相比模拟 e1000、ide 设备,减少了指令翻译和设备模拟的开销。能用 virtio 的地方尽量用 virtio,这是 QEMU 全架构通用的最佳实践。
如果虚拟机网卡需要更高吞吐,可以打开网卡多队列:
<interface type='network'> <source network='default'/> <model type='virtio'/> <driver name='vhost' queues='4'/> </interface>但多队列依赖客户机 CPU 拓扑,在 TCG 模拟下收益不大,甚至可能因为 CPU 调度变复杂而变慢,所以如果是单纯测试就保持单队列。
7.2 共享宿主机目录
ARM64 虚拟机上做交叉编译测试时,经常需要在宿主机和客户机之间传递文件。最快的方式是直接在客户机里挂载 virtiofs:
先在宿主机把目录加到 XML 中:
<filesystem type='mount' accessmode='passthrough'> <source dir='/data/shared'/> <target dir='shared'/> </filesystem>客户机里执行:
mount -t virtiofs shared /mnt/shared如果客户机内核比较老不支持 virtiofs,可以退回到 9p:
<filesystem type='mount' accessmode='mapped'> <source dir='/data/shared'/> <target dir='shared'/> </filesystem>客户机里执行:
mount -t 9p -o trans=virtio,version=9p2000.L shared /mnt/shared9p 的性能一般,胜在兼容性好,RHEL 8 内核自带支持。
7.3 快照、克隆与自动化
做内核实验时,最怕客户机系统被搞坏。用快照可以轻松回滚:
virsh snapshot-create-as arm64-rocky before-kernel-test virsh snapshot-list arm64-rocky virsh snapshot-revert arm64-rocky before-kernel-test快照默认保存在 qcow2 磁盘内部,注意不要无限制生成,否则磁盘会越来越大。克隆虚拟机时,必须重新指定nvram,否则两台虚拟机的 UEFI 变量冲突:
virt-clone --original arm64-rocky --name arm64-rocky-clone --file /data/vms/arm64-rocky-clone.qcow2克隆完成后,用virsh dumpxml arm64-rocky-clone检查<nvram>路径是否已经自动生成新文件。
自动化方面,建议给 ARM64 虚拟机做一套完整的 kickstart 或者 cloud-init 配置。Rocky/Alma 支持把安装参数写入ks.cfg,Ubuntu 支持autoinstall,搭配 libvirt 的--initrd-inject和--extra-args,可以做到一条命令拉起一台带用户和密钥的 ARM64 虚拟机,这是后续做回归测试的神器。
根据我自己的使用体会,ARM64 模拟虚拟机在本地调试中的价值,不亚于一台真实的 ARM 开发板。它的意义不在于跑分,而在于提供了一套可复现、可回滚、随处带走的 ARM64 环境。我个人建议第一次搭的时候,把每一步都记录成 shell 脚本,尤其是virt-install那一段命令要保留好,后续重新建机直接改磁盘路径就能用。最后再分享一个小技巧:如果你在virsh console里发现输出混乱或者没有颜色,可以试试在客户机里把终端类型设置成linux,然后重新登录,很多伪终端显示问题都会迎刃而解。