1. 直通这件事到底在干什么1.1 什么是显卡直通为什么要折腾它显卡直通GPU Passthrough是我玩虚拟化以来觉得最实用也最折腾的一个功能。简单说就是把一张英伟达独立显卡完整地交给一台虚拟机独占让Windows里的游戏、渲染器、深度学习框架直接调用物理GPU而不是靠虚拟显卡转一道手。这些年我在Linux主机上跑Windows虚拟机为了把N卡用上BIOS、内核、libvirt、驱动全折腾了一遍踩了不少坑也把处理办法整理成一套可以照着做的流程。这篇文章适合想在Linux/KVM环境里给Windows虚拟机直通N卡的人不管你是为了打游戏、跑CUDA还是给家里的All-in-One主机加一块独立显卡。相比买一台独立Windows主机直通方案能省下一笔硬件开支也让“一台机器跑两个系统”变成了现实——Linux做主力系统处理日常工作Windows虚拟机里的游戏、专业软件、AI训练环境都跑在同一块屏幕上。先解释一下原理。虚拟化环境里虚拟机的显卡默认是通过软件模拟的比如QEMU自带的VGA、virtio-gpu性能大概只有物理显卡的百分之几。就算你给虚拟机分配了多少核CPU、多少G内存显卡这块始终是瓶颈。而直通做的事情是把物理显卡的PCIe设备直接“借”给虚拟机让虚拟机里的驱动程序直接操作硬件寄存器中间没有宿主机的模拟层。GeForce驱动在虚拟机里能拿到物理显卡的全部特性性能损耗通常控制在5%以内接近裸机水平。1.2 硬件前提和方案选型直通看着高级但硬件不支持就是白搭。核心前提有三个CPU支持内存虚拟化、主板支持DMA重映射、显卡本身支持重置。这三样缺一个后面全是白折腾。CPU方面Intel的VT-x和AMD的SVM也就是AMD-V是所有现代CPU的基本功能一般默认就开了。真正关键的是DMA重映射——Intel叫VT-dAMD叫IOMMU。这项技术负责把设备发出的DMA请求限制在指定的内存区域内让虚拟机可以安全地直接访问物理设备。没有它虚拟机直接操作物理显卡会导致内存错乱甚至整个系统崩溃。主板这一关容易被忽略。不少主板的BIOS里虽然有VT-d开关但默认隐藏或者在“高级/北桥配置”里才能找到。更麻烦的是有些主板对PCIe设备的IOMMU分组支持得很差显卡和声卡、USB控制器被分在同一个IOMMU组里不拆组就没办法单独直通。这个后面我会单独讲怎么验证。显卡方面英伟达从Kepler之后的大部分消费级显卡理论上都能直通但也有几个前提最好是UEFI启动模式下安装的系统显卡需要支持UEFI GOP否则虚拟机启动时可能黑屏显卡的Reset Bug重置失败问题在部分型号上存在——虚拟机关机后显卡无法回到初始状态第二次启动就会卡死。1.3 为什么N卡直通比A卡麻烦A卡在直通这件事上相对省心因为AMD官方对虚拟化场景的驱动限制比较宽松。N卡则不一样英伟达的消费级驱动GeForce驱动里内置了虚拟机检测逻辑——驱动一旦发现自己运行在虚拟机里就直接拒绝加载Windows设备管理器里会显示“该设备有问题Windows已将其停止代码43”。这个限制的根源是商业策略NVIDIA想把虚拟化GPU能力锁定在专业卡Quadro、Tesla上不允许消费级显卡在虚拟机场景发挥完整性能。所以Desktop版驱动在虚拟机里经常报Error 43驱动加载失败显卡变成一块废铁。那是不是就无解了不是。KVM虚拟化平台提供了几条隐藏虚拟化特征的参数可以骗过N卡的驱动检测。核心思路是让虚拟机内部的CPUID信息看起来和物理机一致同时隐藏掉hypervisor的标识。大概两三个配置项就能解决后面我会直接给出改好的XML片段你在libvirt的虚拟机配置里替换进去就行。2. 开工前的硬件检查与BIOS配置2.1 三步检查你的硬件是否支持在写任何配置之前先花十分钟确认硬件底子。我习惯把这套检查分三步每一步都有对应的命令或界面操作不需要装额外工具。第一步进BIOS确认CPU虚拟化和DMA重映射开关。Intel平台找“VT-x”“VT-d”AMD平台找“SVM”“IOMMU”把它们全部设为Enabled。不同品牌主板的位置不一样有的在“Advanced → CPU Configuration”有的在“Overclocking → CPU Features”找不到就用主板的搜索功能或者翻说明书。这里的重点是把VT-d/IOMMU打开否则后面所有配置都没意义。第二步进系统后查看内核是否识别了IOMMU。Linux命令行执行dmesg | grep -e DMAR -e IOMMU如果输出里有类似于“DMAR: IOMMU enabled”的信息说明主板和CPU层面的重映射已经生效。如果没有任何输出多半是BIOS里没开或者内核启动参数还没加。第三步检查显卡的PCIe设备号和IOMMU分组情况。执行lspci -nnk | grep -A 3 -i nvidia记录下显卡和它的音频设备那一行比如10de:2799这种格式就是设备ID后面绑定vfio-pci驱动要用。然后查看IOMMU分组for d in /sys/kernel/iommu_groups/*/devices/*; do g${d#*/iommu_groups/*} g${g%%/*} echo Group $g: $(basename $d) ($(lspci -nns ${d#*/devices/})) done如果显卡、显卡音频、USB控制器等设备被分在同一组说明当前PCIe插槽位置不合理直通时要么连坐要么拆不掉。最常见的解法是换一个PCIe插槽或者调整BIOS里的PCIe槽位分配。这一步做得好后面少走很多弯路。2.2 BIOS里的关键开关除了VT-d/IOMMU还有几个容易踩坑的选项我逐一说明。Above 4G Decoding这个选项必须打开。直通显卡时64位PCIe地址空间需要映射到高于4G的物理内存区域不开启的话系统里装了大容量内存后设备地址分配容易冲突虚拟机开机直接卡死。CSM兼容性支持模块建议关闭。CSM是传统BIOS引导的兼容层开启时会强制设备使用传统Option ROM方式初始化。N卡直通时传统ROM和UEFI GOP之间经常打架导致虚拟机启动后显卡没输出。关闭CSM全UEFI模式启动直通的成功率会高很多。Resizable BAR可调整大小的BAR这台虚拟机上建议关闭。Resizable BAR是近几年新加的PCIe特性它允许CPU访问GPU的完整显存映射。问题在于某些版本的N卡驱动在虚拟化环境中开启Resizable BAR后会触发重置异常或报错。我的实测经验是物理机上开着它没毛病但直通到虚拟机里时先关了最稳。SR-IOV不需要开。SR-IOV是把一张卡虚拟成多个功能的特性完全不属于我们讨论的PCIe设备直通范畴。如果你看到这个选项保持默认关闭即可。这些选项的位置因BIOS版本而异但核心原则就一条虚拟化相关的全开兼容层相关的全关特殊PCIe特性先关。改完保存重启进系统用dmesg确认。2.3 双显卡策略为什么最好有两张卡这是我踩过最深的坑。如果你只有一张N卡而且宿主机靠它输出显示那直接直通它会导致宿主机黑屏——显卡被虚拟机独占宿主机无卡可用。所以绝大多数成功的直通方案里宿主机都有一个显示输出方案常见有三类第一类核显方案。CPU带核显Intel的UHD系列或者AMD的APU系列宿主机用核显输出独立显卡专门直通给虚拟机。这是最干净、最推荐的做法。BIOS里把核显设置为默认显示输出然后在宿主机上正常用核显就行。注意要在BIOS里把“Primary Display”设为iGPUIGFX否则有些主板的核显在插了独显后会自动禁用。第二类双N卡方案。宿主机用一张低端卡比如一张旧卡或者亮机卡输出高端卡直通。这种方式也能用但需要注意每张卡的IOMMU分组是否独立。两张同型号卡在同一个IOMMU组里也不是没见过那是主板PCIe分配问题换槽位解决。第三类无头方案。宿主机不接显示器靠SSH远程管理直通卡平时不参与显示。这种方式省了一张卡但排错难度明显变高一旦显卡直通异常你连个图形界面都看不到。个人建议如果你还在规划安装优先走核显直通卡的双硬件方案。这不是什么高深道理纯粹是排错方便。我第一次做直通时只有一张卡结果系统黑屏了一下午最后老老实实把一张旧卡插上问题才真正开始可解。3. 宿主机配置从内核参数到vfio-pci绑定3.1 开启IOMMUGRUB参数修改进入Linux系统后第一步是往内核参数里加上IOMMU开关。我用的是最通用的GRUB引导方案Ubuntu、Debian、Fedora这些发行版都适用。编辑/etc/default/grub找到GRUB_CMDLINE_LINUX这一行在引号内追加参数Intel平台: GRUB_CMDLINE_LINUXintel_iommuon iommupt AMD平台: GRUB_CMDLINE_LINUXamd_iommuon iommuptiommupt的意思是只对直通的设备开启DMA重映射其他设备使用传统直通模式可以显著降低IOMMU开启后的性能损耗。我试过不写iommupt外设性能确实会掉一点尤其是网卡和NVMe硬盘。加了之后日常使用几乎无感。保存后更新引导配置sudo update-grubUbuntu/Debian用update-grubFedora/RHEL系用sudo grub2-mkconfig -o /boot/grub2/grub.cfg。改完重启系统再执行一次dmesg | grep -e DMAR -e IOMMU确认IOMMU已经生效。3.2 绑定vfio-pci驱动重启后系统已经支持IOMMU了但显卡还是被内核的nvidia或nouveau驱动加载。直通的前提是宿主机的驱动不去碰这张卡把它交给专属的vfio-pci驱动接管。vfio-pci是一个用户态驱动框架它本身不对设备做什么初始化只提供IOMMU重映射和中断重映射的功能。设备绑定到它之后宿主机就“看不到”这张显卡的业务能力了只看到一串PCIe设备号和BAR地址。创建/etc/modprobe.d/vfio.conf写入options vfio-pci ids10de:2799,10de:1aeb其中10de:2799是显卡的设备ID10de:1aeb是同一张卡上音频设备的ID。不同显卡型号的ID不一样用lspci -nnk查询自己那张卡的ID填进去。下一步是把vfio模块加入initramfs并阻止N卡驱动加载。不同发行版操作不同。Ubuntu/Debian系创建/etc/initramfs-tools/modules加入vfio vfio_iommu_type1 vfio_pci然后执行sudo update-initramfs -uFedora/RHEL系创建/etc/modprobe.d/local.conf加入install nvidia /bin/false同时执行dracut --force重新生成initramfs。这里有个容易漏掉的细节initramfs是启动初期就要加载的模块仓如果vfio模块没有进initramfs系统启动时显卡已经先被nvidia或nouveau接管了你再怎么modprobe都没用因为设备已经被占用了。3.3 验证驱动绑定是否正确重启系统后执行lspci -nnk | grep -A 3 -i nvidia如果输出里显示Kernel driver in use: vfio-pci说明绑定成功显卡已经被宿主机释放等待虚拟机取用。如果显示还是nouveau或nvidia检查一下黑名单文件。创建一个/etc/modprobe.d/blacklist-nvidia.conf把N卡驱动写进黑名单blacklist nouveau blacklist nvidia然后再执行update-initramfs -u或dracut --force再重启一次。这一步的输出决定成败别嫌麻烦多看几遍。我遇到最常见的失败原因就是initramfs没更新看起来配置了vfio实际启动时没加载。4. 虚拟机的完整直通配置4.1 创建虚拟机和基础配置我用的是libvirt QEMU方案图形化管理用virt-manager命令行管理用virsh。如果你的发行版还没装Ubuntu执行sudo apt install qemu-system-x86 libvirt-daemon-system libvirt-clients virt-manager ovmfFedora执行sudo dnf install qemu-kvm libvirt virt-manager edk2-ovmf装好之后用virt-manager新建一个虚拟机系统选择Windows镜像选择Windows安装ISOCPU和内存根据你的硬件决定。我建议至少给4核8G起步游戏或AI训练场景给8核16G这些是Windows本身的需求和直通关系不大。关键点在于两个主板固件必须选“UEFI”OVMF不能选BIOS模式。N卡在UEFI模式下直通最稳定显卡的GOP初始化逻辑也是走UEFI路径。第二安装Windows时先用默认显卡比如virtio或QXL完成系统安装等Windows装好后再插入物理显卡否则安装过程可能因为驱动问题中断。安装好Windows后关机打开virt-manager里的“添加硬件 → PCI Host Device”选择你的N卡设备通常同时选上它的音频设备。如果virt-manager里看不到显卡确认上面的vfio-pci绑定成功后再刷新。4.2 骗过N卡驱动XML配置修改这是整个流程里最关键的一步所谓“N卡驱动检测虚拟机”的问题就在这个阶段解决。打开虚拟机的XML配置virsh edit 虚拟机名定位到features和cpu部分按下面的模板修改。首先确保CPU模式是host-passthrough让虚拟机里的CPUID信息直接暴露宿主机的物理CPU特性不存在虚拟化CPU标识cpu modehost-passthrough checknone topology sockets1 dies1 cores4 threads2/ /cpu在features里加入隐藏hypervisor的关键参数features acpi/ hyperv relaxed stateon/ vapic stateon/ spinlocks stateon retries8191/ vendor_id stateon value1234567890ab/ /hyperv kvm hidden stateon/ /kvm ioapic driverkvm/ /features这三个参数的作用分别解释一下hidden stateon/是KVM隐藏自身标识的总开关。它会去掉CPUID中hypervisor位让Windows认为自己在物理机上运行。vendor_id stateon value1234567890ab/是Hyper-V的vendor ID伪装。N卡的驱动会读取Hyper-V相关的CPUID叶子如果发现vendor ID不是微软的默认值会判定为kvm并拒绝启动。这个值可以自己填任意12位十六进制字符串但不能以Microsoft开头那反而会露馅。ioapic driverkvm/是IO-APIC中断控制器配置部分较新的N卡驱动在高版本Windows上会因为IOAPIC版本不匹配而报错加上这条能规避。改完保存启动虚拟机进入Windows安装最新版N卡驱动然后重启。如果一切正常设备管理器里显卡应该正常显示没有黄色感叹号分辨率也能调到正常值。如果在任务管理器里能看到GPU占用说明驱动已经启用直通成功。4.3 直通后的性能核验驱动装上后性能到底什么水平我个人实测的结果是CUDA计算、3D渲染类负载基本无损游戏性能略有下降但幅度在0~5%之间取决于CPU是否给力、内存延迟、NVMe速度等因素。如果你的性能下降超过10%排查方向通常是CPU调度问题或内存大页没配。顺带提一下内存大页Hugepages和CPU Pinning这两个不是直通的必需项但对性能影响明显。在宿主机上预留内存做1G大页再把物理CPU核心固定给虚拟机可以显著降低虚拟化开销。具体配置方法memoryBacking hugepages page size1 unitGiB/ /hugepages /memoryBacking同时在宿主机/etc/default/grub的GRUB_CMDLINE_LINUX里追加default_hugepagesz1G hugepagesz1G hugepages8这里的hugepages8表示预留8个1G大页数值根据你给虚拟机的内存大小调整。CPU Pinning配置则是把宿主机的物理核心绑定到虚拟机的vCPU上cputune vcpupin vcpu0 cpuset2/ vcpupin vcpu1 cpuset6/ vcpupin vcpu2 cpuset3/ vcpupin vcpu3 cpuset7/ /cputunecpuset里的编号是宿主机的物理核心编号建议用物理核心里成对超线程编号绑定比如2和6是一对超线程3和7是一对。具体对应关系用lscpu -e查。这些配置属于锦上添花如果只是验证直通能不能用可以先不弄。5. 常见问题与排查技巧实录5.1 NVIDIA代码43错误最常遇见的坑只要用N卡直通就没有不遇代码43的。这个问题就是显卡驱动在虚拟机里拒绝启动设备管理器里显示“Windows已停止此设备因为其报告了问题代码43”。常见的排查顺序是先确认是否已经加了hidden和vendor_id配置——这两个是必要条件再检查host-passthrough是否生效——如果CPU模式是customCPUID特征不完整驱动会从这里识别到虚拟化痕迹最后确认Windows版本和驱动版本是否太新——部分新版Windows更新会改变CPUID叶子策略导致旧的隐藏参数失效。另外还有个小技巧某些卡在BIOS UEFI模式下如果开着Resizable BAR也会触发代码43。先把BIOS里这个选项关了如果正常再考虑开启时不添加iommupt等变体。如果以上都排查完了依然报43还有一个较新的解法给虚拟机添加vendor_id改为英伟达可能期望的微软默认值。虽然理论上不会检测默认值但实测某些大版本驱动对默认值更友好。把value1234567890ab改成valueMicrosoft MSv试试注意总长必须12位。5.2 IOMMU分组不理想如果显卡和USB控制器、NVMe硬盘在同一个IOMMU组里你直通显卡时这个组里所有设备都会一起被VFIO接管宿主机上对应的设备就会消失。如果这组里有系统盘直接无法启动。IOMMU分组由硬件决定软件层面很难改变。唯一能做的调整是换PCIe槽位。主板的PCIe槽位分配规则是固定的直通设备往往有独立的IOMMU组。如果插槽换不了只能参照ACSSACS override补丁思路——但这涉及内核动手术和风险控制我建议普通用户不要碰。你甚至可以接受把声卡、网卡一起直通进虚拟机但前提是宿主机不需要它们。5.3 虚拟机关机后显卡无法重新初始化这个问题的表现是虚拟机第一次启动正常但Windows关机后再次启动虚拟机时黑屏显卡设备在宿主机侧“卡死”了。这是N卡Reset Bug的经典症状显卡在虚拟机关机时没有正确重置到初始状态。解决方法有两个方向一是确保虚拟机是“优雅关机”通过Windows的开始菜单正常关机而不是强制kill虚拟机进程。强制关闭时QEMU没有机会发送关机信号设备寄存器状态不可控重置很容易失败。二是给libvirt配置hook脚本在虚拟机启动前强制重新绑定显卡设备。假如你每次关闭虚拟机虚拟机管理进程退出后用以下命令手动重置设备echo 1 /sys/bus/pci/devices/0000:01:00.0/reset这里的0000:01:00.0替换成你显卡的PCI地址。如果这个reset文件存在说明设备支持软复位。在libvirt里写hook脚本每次启动前执行这条命令可以大幅降低重启黑屏的概率。5.4 直通后宿主机分辨率异常或黑屏如果你只有一张卡直通后宿主机自然会黑屏这是预期行为。但如果你有核显或第二张卡宿主机黑屏则是优先级设置被破坏。问题出现在BIOS的Primary Display设置被重置或者核显被自动禁用了。进BIOS把Primary Display设为iGPU或IGFX然后再试一次。另一个关系到内核参数确保核显设备没有被vfio.conf里的id匹配到。用lspci -nnk确认核显的驱动还是i915或者amdgpu如果变成vfio-pci自然是没办法显示了。5.5 虚拟机的鼠标键盘无响应这个是直通的附属问题——你把显卡直通进Windows后输入设备如果走QEMU默认的虚拟键盘鼠标理论上也能用但延迟明显。解决办法是USB直通在宿主机上把USB控制器或具体USB设备直通给虚拟机。hostdev modesubsystem typeusb managedyes source vendor id0x046d/ product id0xc539/ /source /hostdev这样鼠标键盘直接连到Windows跟物理机没任何区别延迟感会明显下降。5.6 快速排除速查表问题现象排查方向解法00:00.0显卡驱动未绑定vfio内核参数/initramfs确认vfio.conf、grub参数、update-initramfs/dracutWindows报代码43虚拟化特征隐藏不彻底检查hidden、vendor_id、cpu host-passthrough虚拟机启动黑屏显卡重置/引导方式不对确认UEFI模式、关闭CSM、关闭Resizable BAR宿主机黑屏核显被禁用BIOS设核显为主输出确认核显没被vfio绑定虚拟机重启失败Reset Bug手动软复位、hook脚本、优雅关机IOMMU分组连带主板插槽分配换PCIe插槽6. 实战中积累的一些心得最后聊几个我实际折腾下来觉得最值得分享的经验。第一尽量用一个干净的Linux发行版做宿主机。不是说不能在NAS系统或桌面系统上折腾而是依赖越少、默认服务越少出问题的概率越低。我自己用的是Ubuntu Server做底子桌面另开一个虚拟机根本不影响日常使用。第二不要一上来就追求大页和CPU Pinning。先把裸直通跑通确认驱动能装上、性能能接受再一步步加这些优化项。一次改太多出了问题很难定位是哪个配置引起的。我见过不少人在一步没走通时就开始叠加各种参数最后整个环境一团乱只能重装。第三直通这块儿买卡之前可以先查一下对应型号在社区的反馈尤其是Reset Bug的情况。并不是越贵的卡直通越省心有些品牌型号的Reset Bug特别顽固而有些老型号反而完美。如果你在筹划新装机把直通需求作为选型的一个维度省下的调试时间远超你的想象。第四Windows虚拟机里记得装完整版virtio驱动。系统刚装好时用的存储、网络驱动都是模拟的装完virtio驱动后磁盘读写和网络性能会明显上升。这个虽然是基础操作但很多人因为直通显卡成功后兴奋过头把这个忘了后面跑个大文件传输才发现性能不对。第五学会看dmesg和QEMU日志。直通失败时的关键线索几乎都在日志里比如vfio-pci 0000:01:00.0: Failed to setup container这类信息直接对应到设备状态问题。我曾经花一个多小时反复改XML最后发现是显卡Reset失败日志里早就写明了。这些经验看起来零散但都是拿时间换来的。直通这个功能本质上不是什么黑魔法它就是IOMMU、VFIO、QEMU三个组件协同工作的结果。把每个组件的状态逐一确认成功也只是时间问题。如果你正在折腾途中别急按这个流程一步步走应该能比我第一次省下大把时间。
阅读完成 · 觉得有帮助?