PCIe 硬件直通 (VFIO) 深度实战

将 Linux 作为日常的主系统(Host),同时又需要运行某些仅支持 Windows 的 3A 大作或特定生产力软件,是很多极客与高级用户的刚需。传统的虚拟机显卡模拟在 3D 性能和 DirectX/Vulkan 支持上极其孱弱。

PCIe 直通(PCIe Passthrough / VFIO) 允许我们将物理显卡、声卡或 NVMe 硬盘的控制权完全绕过宿主机内核,直接穿透并赋予虚拟机(Guest)。凭借该技术,虚拟机能够发挥出物理显卡 98%~99% 的原生性能!


1. 核心架构与硬件前置要求

[ 宿主机 (Linux Host) ]
  ├── 内核模块: vfio-pci (强行接管目标 PCIe 设备)
  └── CPU IOMMU 控制器 (物理隔离硬件 DMA 与内存访问)

          ▼ Direct PCIe Handover
[ 虚拟机 (Windows Guest) ]
  ├── 独占访问物理 PCIe 显卡 (Nvidia / AMD GPU)
  └── 直接加载原生 Windows 显卡驱动

硬件与平台必备要素

  1. CPU 支持:Intel 平台需支持 Intel VT-xIntel VT-d;AMD 平台需支持 AMD-VAMD-Vi(在 BIOS/UEFI 中开启 SVM / Virtualization 以及 IOMMU / VT-d)。
  2. 主板 PCIe 拓扑:主板固件必须具备良好的 IOMMU Group 隔离设计(即目标显卡不能与主板网卡、SATA 控制器混在一个 IOMMU 组里)。
  3. GPU 硬件:支持 UEFI GOP 引导的现代化 GPU。

2. IOMMU 启用与组隔离检测

2.1 开启系统 IOMMU 支持

编辑引导加载器配置(以 GRUB 为例,修改 /etc/default/grub 中的 GRUB_CMDLINE_LINUX_DEFAULT):

Intel CPU:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt"

AMD CPU:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt"

注:iommu=pt (Pass Through) 可以跳过不进行直通的设备的 DMA 转换,提升宿主机 I/O 性能。

更新 GRUB 并重启系统:

sudo update-grub  # Ubuntu/Debian
# 或 sudo grub-mkconfig -o /boot/grub/grub.cfg # Arch Linux

2.2 运行 IOMMU 组隔离检测脚本

重启后,创建一个 Bash 脚本检查 IOMMU 分组情况:

#!/bin/bash
# save as iommu_check.sh
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do
  n=${d#*/iommu_groups/}
  n=${n%%/*}
  printf 'IOMMU Group %s ' "$n"
  lspci -nns "${d##*/}"
done

关键输出示例

IOMMU Group 13 03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3070] [10de:2484] (rev a1)
IOMMU Group 13 03:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)
ℹ️ IOMMU 隔离铁律

同属于同一个 IOMMU 组里的所有 PCIe 设备必须整体绑定并直通给虚拟机,或者全部留给 Host。在上例中,03:00.0 (GPU) 和 03:00.1 (音频) 在 Group 13 中,直通时必须将这两个设备同时解绑并绑定给 VFIO。

如果显卡与宿主机的其他关键设备(如 PCIe 网卡/SATA 控制器)被混在同一个 IOMMU 组中,可尝试更换主板 PCIe 插槽,或在引导参数中加上 ACS 重定向补丁 pcie_acs_override=downstream,multifunction(注意:ACS 补丁存在潜在安全隔离风险)。


3. VFIO 驱动绑定与设备隔离

3.1 获取设备的 Vendor ID 与 Device ID

如上步骤所示,提取 RTX 3070 及其 Audio Controller 的硬件 ID:

  • 显卡 ID: 10de:2484
  • 音频 ID: 10de:228b

3.2 配置 VFIO 驱动接管

创建 /etc/modprobe.d/vfio.conf 文件:

options vfio-pci ids=10de:2484,10de:228b

为了确保 vfio-pci 驱动在宿主机显卡驱动(如 nvidia / nouveau / amdgpu)之前抢先加载接管设备,需要在 initramfs 模块配置中提前置入 vfio 模块:

Ubuntu / Debian (/etc/initramfs-tools/modules):

vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

更新 initramfs 并重启:

sudo update-initramfs -u

Arch Linux (/etc/mkinitcpio.conf): 在 MODULES=(...) 数组的最前端添加:

MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd)

重新生成 initcpio:sudo mkinitcpio -P

Fedora / RHEL (/etc/dracut.conf.d/vfio.conf):

add_drivers+=" vfio vfio_iommu_type1 vfio_pci "

重新生成 dracut:sudo dracut --force --kver $(uname -r)

3.3 验证接管状态

重启后运行 lspci -nnk -s 03:00.0,检查 Kernel driver in use 项:

03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3070]
	Subsystem: Micro-Star International Co., Ltd. [MSI] Device 3902
	Kernel driver in use: vfio-pci
	Kernel modules: nvidiafb, nouveau, nvidia

显示 Kernel driver in use: vfio-pci 即表示隔离成功!


4. 单显卡直通 (Single GPU Passthrough) 与双显卡直通

4.1 双显卡直通(最稳定方案)

  • 拓扑:Host 使用 CPU 核显(Intel HD / AMD APU)或一张低功耗亮机卡显示 Linux 桌面;独显(RTX 3070)隔离给 Windows 虚拟机。
  • 优点:Host 与 Guest 互不干扰,随时启动关机虚拟机,操作极其稳定。

4.2 单显卡直通(Single GPU Passthrough)

如果整机只有一张独立显卡,也能实现直通。其核心原理是利用 libvirt 提供的 Hook 脚本/etc/libvirt/hooks/qemu):

用户启动 Windows VM

  ├─> 1. Hook 脚本自动杀掉 Display Manager (GDM/SDDM) 并卸载 N 卡驱动
  ├─> 2. 动态将单显卡解绑并绑定到 vfio-pci 驱动
  ├─> 3. KVM 虚拟机启动,占用显卡输出

Windows VM 关机

  ├─> 4. Hook 脚本将显卡驱动重新绑定回 nvidia/amdgpu
  └─> 5. 重新启动 GDM/SDDM 恢复 Linux 桌面

由于单显卡直通脚本编写复杂且依赖不同发行版,建议使用开源的 libvirt_hooks 自动化构建工具链。

💡 单显卡与次级 PCIe 显卡 VBIOS Dump 挂载

在单显卡直通或部分笔记本/主板次级插槽上,显卡的 VBIOS 可能会在宿主机 Boot 阶段被修改破坏导致虚拟机黑屏。此时需要在 Linux 宿主机上导出原厂 VBIOS ROM,并在 XML 的 <hostdev> 节点中手动指定: <rom file='/var/lib/libvirt/vbios/rtx3070.rom'/>


5. Looking Glass 零延迟黑科技

在传统的直通方案中,Guest 显卡的画面输出需要通过 HDMI/DP 线连接到另一台显示器,或者在显示器上手动切换信号源。

Looking Glass 是一套突破性的开源方案:它在宿主机与虚拟机之间开辟一块 KVMFR (KVM Framebuffer) 共享内存。虚拟机中的游戏画面由物理显卡渲染后直接无压缩刷入内存,Linux 宿主机以极低延迟(< 1ms)在窗口中读取并渲染画面!

[ Windows Guest 物理显卡 ] ──(渲染画面)──> [ KVMFR 共享内存 /dev/shm/looking-glass ]
                                                     │ (秒级直接读取)
[ Linux Host 桌面 ] <──────(Looking Glass Client)───┘

5.1 配置 KVMFR 共享内存

在宿主机创建 /etc/tmpfiles.d/looking-glass.conf

# 格式: Type Path Mode User Group Age Argument
f /dev/shm/looking-glass 0660 susu kvm -

在虚拟机的 XML 配置文件(virsh edit <vmname>)的 <devices> 节点内部引入原生的 <shmem> 共享内存设备(以 32MB 内存为例,支持最高 4K @ 60Hz):

<devices>
  <!-- ...其他设备配置... -->
  <shmem name='looking-glass'>
    <model type='ivshmem-plain'/>
    <size unit='M'>32</size>
  </shmem>
</devices>

在 Windows 虚拟机内安装 Looking Glass Host 客户端驱动与服务,在 Linux 宿主机运行 looking-glass-client,即可体验如同原生 Linux 窗口一般的无缝 Windows 3A 游戏!


6. 虚拟机游戏极致调优策略

为了消除物理硬件虚拟化带来的微小帧抖动(Stuttering),必须进行如下极致性能调优:

6.1 CPU 拓扑与 CPU Pinning (绑核)

防止虚拟 CPU 跨 NUMA 节点或跨 CPU 核心调度造成 Cache Miss:

<vcpu placement='static'>12</vcpu>
<cputune>
  <vcpupin vcpu='0' cpuset='2'/>
  <vcpupin vcpu='1' cpuset='8'/>
  <vcpupin vcpu='2' cpuset='3'/>
  <vcpupin vcpu='3' cpuset='9'/>
  <!-- ...配对物理核与其 SMT 超线程逻辑核... -->
  <emulatorpin cpuset='0,6'/>
</cputune>

6.2 开启 Hugepages (大页内存)

预先在 Linux 宿主机上分配 1GB 或 2MB 大页内存,避免虚拟机访问内存时的频繁页表转换:

# 分配 16 个 1GB 大页 (即 16GB 内存)
echo 16 | sudo tee /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages

在 XML 中启用大页:

<memoryBacking>
  <hugepages/>
</memoryBacking>

6.3 绕过反作弊与 Code 43 检测

虽然 NVIDIA 官方从 465+ 驱动版本开始已主动解除了消费级显卡在虚拟机中的 Code 43 限制,但某些在线游戏(如 EAC、BE 反作弊)仍会检测 Hypervisor 签名并拒绝启动。可通过修改 XML 隐藏 KVM 状态:

<features>
  <hyperv>
    <relaxed state='on'/>
    <vapic state='on'/>
    <spinlocks state='on' retries='8191'/>
    <vendor_id state='on' value='GenuineIntel'/>
  </hyperv>
  <kvm>
    <hidden state='on'/>
  </kvm>
</features>

7. 总结

凭借 VFIO PCIe 直通Looking Glass 共享内存 以及 CPU Pinning / 巨页内存调优,Linux 用户完全能够兼顾安全可控的 Linux 工作环境与极致性能的 Windows 游戏娱乐。

Navigation