1. 先把需求拆开:你说的“用上 GPU”到底是哪一种
虚拟机里跑 Ubuntu,想借用宿主机那块显卡,这个问题在我这些年接的咨询里出现频率相当高。很多人上手第一步就是打开虚拟机设置找“显卡”选项,翻遍了也没看到能指定物理 GPU 的地方,于是开始怀疑是不是软件版本不对、是不是少装了什么插件。实际情况是,VMware Ubuntu 虚拟机使用宿主机 GPU 资源这件事本身分成两条完全不同的技术路线,它们的原理、前提、能达成的效果差得非常远,如果不先分清自己要的是哪一种,后面所有操作都是白费劲。
我一般会让对方先回答一个问题:你在 Ubuntu 里到底要干嘛?是要跑桌面、看视频不卡,还是要开 Blender 做三维建模、跑 OpenGL 渲染,还是要跑 PyTorch 训练模型、跑 CUDA 程序?这三个诉求对应的方案完全不同,甚至可以说,第三个诉求在 VMware Workstation 这个平台上根本没有解。把需求分清楚,比急着改配置文件重要得多。
这篇文章我按“方案一:Workstation 内置的虚拟 3D 加速通道”和“方案二:PCIe 直通,把整块物理卡交给 Ubuntu”两条主线来写,中间穿插一条很多人忽略的折中路线。方案一零成本、十分钟能验证,适合图形界面和轻量 OpenGL;方案二要换虚拟化平台、要折腾内核参数,但能拿到接近原生的 CUDA 算力。两条路的坑我都踩过,下面会一条条拆。
1.1 三个层次:显示加速、图形渲染、通用计算
我把虚拟机用 GPU 的需求粗分成三层,从上往下,难度递增,能拿到的性能递减。
第一层是显示加速。目标只是让 Ubuntu 桌面拖动窗口不撕裂、分辨率能上到 2K、看 4K 视频不掉帧。这一层其实是 CPU 在做光栅化,GPU 参与度很低,Workstation 自带的虚拟显卡配合open-vm-tools-desktop基本够用,很多时候你甚至不需要开 3D 加速。
第二层是图形渲染。跑 OpenGL 应用、跑 glmark2、跑轻量级的 CAD 查看器、跑一些基于 Qt 的图形程序。这一层必须打开 Workstation 的 3D 加速,让客户机里的 Mesa 通过vmwgfx驱动把 OpenGL 指令转发到宿主机,由宿主机真实的 GPU 完成渲染再回传画面。这条路能跑通,但性能是打折的,而且有明确的版本上限。
第三层是通用计算。CUDA、OpenCL、ROCm、TensorFlow、PyTorch、视频硬编码(NVENC)、大模型推理。这一层的关键不是“画面”,而是显存和计算单元的直接访问,虚拟显卡完全无能为力,必须走 PCIe 直通,让 Ubuntu 看到一张货真价实的物理显卡,装原厂驱动。
提示:判断自己在哪一层,最简单的办法是想一下“这个程序需不需要知道显卡型号”。
nvidia-smi能看到卡,就是第三层;只有glxinfo能看到渲染器名字,那是第二层;连glxinfo都显示软件渲染,那就是第一层。
我见过太多人想用方案一去跑 CUDA,折腾两天最后一查nvidia-smi报“command not found”,才意识到方向从一开始就错了。所以下面这张表建议先看一遍,再决定往下读哪一章。
| 需求层次 | 典型场景 | VMware Workstation 能否满足 | 推荐路线 |
|---|---|---|---|
| 显示加速 | 桌面流畅、多显示器、高清视频 | 能,原生支持 | 装 open-vm-tools-desktop 即可 |
| 图形渲染 | OpenGL 应用、轻量建模、界面预览 | 能,但有性能损耗和版本天花板 | 方案一 |
| 通用计算 | CUDA、PyTorch、视频编码、推理 | 不能 | 方案二 |
| 混合场景 | 一边建模一边渲染 | 部分能,需要权衡 | 方案一 + 折中路线 |
1.2 为什么虚拟机里永远看不到真实的显卡型号
Workstation 给虚拟机提供的显卡是一块虚拟设备,PCI 标识是 VMware 自己的厂商 ID,客户机里的lspci看到的是一行SVGA II Adapter。这块虚拟卡的作用是接管客户机的显示输出,把帧缓冲通过共享内存交给宿主机上的 MKS(鼠标键盘屏幕)进程去合成。
开了 3D 加速之后,虚拟卡会多出一条通路:客户机里 Mesa 的svga驱动把 OpenGL 命令序列化,通过 VMCI 或共享内存送到宿主机,宿主机的 MKS 用本机真实的 OpenGL 驱动(也就是你装的显卡驱动)去执行,渲染结果再作为帧数据回传。所以你在 Ubuntu 里glxinfo看到的渲染器名字是SVGA3D开头,而不是GeForce或Radeon。
这个设计的本质是命令转发,不是设备透传。宿主机 GPU 确实参与了工作,但客户机全程不知道它的存在,拿不到显存地址,装不了原厂驱动,自然也跑不了 CUDA。理解这一点,后面所有的行为就都能解释了:为什么帧率上不去、为什么某些 OpenGL 扩展不支持、为什么nvidia-smi找不到设备。
还有一层容易被忽略:这条转发链路上的每一环都可能成为瓶颈。宿主机的 OpenGL 驱动版本、MKS 的编译选项、客户机 Mesa 的版本、虚拟显存的大小、甚至宿主机的电源管理策略(独显被降频),都会影响最终表现。这也是为什么同样配置的两台机器,一个人跑 glmark2 能到 2000 分,另一个人只有 800 分。
1.3 两条路线的全景对比
在动手之前,先把两条路线的代价摆清楚,避免做到一半发现方向不对。
| 对比项 | 方案一:内置 3D 加速 | 方案二:PCIe 直通 |
|---|---|---|
| 虚拟化平台 | VMware Workstation Pro | KVM/QEMU + VFIO 或 ESXi |
| 硬件要求 | 无特殊要求 | CPU 支持 VT-d/AMD-Vi,主板支持 IOMMU |
| 显卡要求 | 任意 | 需要至少两块显卡(一块给宿主输出) |
| 客户机看到的设备 | SVGA II 虚拟显卡 | 真实物理显卡 |
| 能否装原厂驱动 | 不能 | 能 |
| CUDA 支持 | 不支持 | 完整支持 |
| OpenGL 上限 | 受转发链路限制,通常到 4.x 级别 | 与原生一致 |
| 性能损耗 | 图形 40%~70%,计算不可用 | 图形 5%~10%,计算 3%~5% |
| 配置耗时 | 10 分钟 | 半天到两天 |
| 主要风险 | 几乎无 | 分组不理想、单卡直通失败、vBIOS 问题 |
这张表最需要关注的是“显卡要求”和“配置耗时”两行。方案二看起来香,但它有个硬前提:宿主机得有一块能独立输出画面的显卡。如果你的机器只有一块独显,又想让这块独显直通给 Ubuntu,那么在直通生效期间宿主机是没有画面的,后面的操作全靠 SSH。这种“单卡直通”不是不能做,但对新手极不友好。
我的建议比较直接:如果需求停在第二层,老老实实用方案一,把时间省下来干正事;如果明确要跑 CUDA,那就别在 Workstation 上耗了,直接上方案二,Workstation 在这件事上帮不了你。
2. 方案一:把 Workstation 的 3D 加速通道真正打开
这一章是给那些“只是想跑个图形程序”“想看看 Ubuntu 桌面能不能顺滑一点”的人准备的。整个流程加起来不超过半小时,绝大多数失败案例的原因都可以归结为三个字:没生效。配置看起来都改了,glxinfo一跑还是软件渲染。
我先把结论放前面:方案一要生效,需要宿主机驱动正常 + .vmx 参数正确 + 客户机驱动栈完整三个条件同时满足,缺一不可。下面按这三个条件依次拆。
2.1 宿主机侧的准备与自检
宿主机这一侧的准备工作经常被跳过,但它是整条链路的地基。MKS 最终是用宿主机的 OpenGL 驱动去执行渲染的,如果宿主机自己都没装好显卡驱动,虚拟机里再折腾也没用。
先确认宿主机显卡驱动状态。Windows 宿主机打开设备管理器,看显示适配器下面有没有黄色感叹号,右键属性看驱动版本;顺手跑一下dxdiag,在“显示”标签页确认 DirectX 功能级别正常。Linux 宿主机用glxinfo -B看渲染器是不是独显名字,如果显示llvmpipe说明走的是软件渲染,得先把驱动装好。
第二件事是确认 Workstation 版本。3D 加速的能力上限跟版本强相关,较新的版本对 OpenGL 4.x 级别和 DirectX 11 级别的虚拟图形支持更完整,老版本可能只到 OpenGL 3.3。虚拟机的硬件兼容性版本也要跟着提上去,在虚拟机设置里把“兼容性”调到当前 Workstation 支持的最高档,否则新特性会被硬件版本卡住。
第三件事是宿主机电源管理。笔记本上这一点特别明显:独显被系统判定为空闲后会自动降频甚至断电,虚拟机里的渲染请求过去时 GPU 还在“醒神”,表现出来就是前几秒卡顿、后面才正常。可以的话把宿主机电源计划切成高性能,或者把 Workstation 进程加到显卡控制面板的“高性能”列表里。
注意:宿主机如果有双显卡(核显 + 独显),要确认 Workstation 用的是哪一块。Windows 上可以在“图形设置”里给
vmware.exe指定“高性能”,不然默认可能被分配到核显,性能差一大截。
2.2 .vmx 关键参数逐条拆解
Workstation 的图形相关配置在虚拟机设置界面只能勾一个“加速 3D 图形”,剩下的大量参数藏在.vmx文件里。这个文件在虚拟机的存放目录下,用文本编辑器打开就能改,改之前先把虚拟机关机——运行中改会被覆盖。
下面这组是我目前用得最稳的配置,逐条说明:
mks.enable3d = "TRUE" mks.enableGLES = "TRUE" mks.gl.allowBlacklistedDrivers = "TRUE" svga.autodetect = "FALSE" svga.vramSize = "536870912" svga.maxWidth = "3840" svga.maxHeight = "2160" vmotion.checkpointFBSize = "536870912" mks.maxRefreshRate = "60"mks.enable3d是总开关,等价于界面上的“加速 3D 图形”勾选框,必须为 TRUE。这一项如果为 FALSE,后面所有参数都没意义。
mks.enableGLES打开 OpenGL ES 通路。有些图形栈(尤其是基于 Qt 或 Wayland 的现代应用)走的是 GLES 而不是桌面 OpenGL,不开这一项可能会出现窗口黑屏、界面渲染异常。
mks.gl.allowBlacklistedDrivers是应急开关。Workstation 内部维护了一份宿主驱动黑名单,某些版本的驱动会被判定为“不兼容”而强制退回软件渲染。如果你确认宿主机驱动工作正常、glxinfo也没问题,但虚拟机里就是不加速,把这个设成 TRUE 大概率能解决。
svga.autodetect设为 FALSE 是为了让下面的显存和分辨率参数生效。设为 TRUE 时 Workstation 会自动决定显存大小,通常是 128MB 或 256MB,跑高分辨率或者多显示器会不够。
svga.vramSize是虚拟显存,单位是字节。536870912就是 512MB。这个值不是越大越好,超过一定范围对性能没有帮助,反而会占用宿主机内存。我一般按分辨率给:1080p 给 256MB,4K 或多显示器给 512MB 到 1GB。如果客户机里要开比较大的纹理或者多窗口并行渲染,可以再往上调一档。
svga.maxWidth和svga.maxHeight是虚拟显示的最大分辨率。这两个值如果你不改,虚拟机里设置分辨率时会发现超过某个值就被截断了。4K 场景下设成 3840×2160,超宽屏可以设 5120×1440。
vmotion.checkpointFBSize这个参数名字很误导,它跟迁移没关系,实际控制的是快照和挂起时保留的帧缓冲大小。必须和svga.vramSize保持一致,否则虚拟机挂起再恢复可能黑屏。这个坑我踩过一次,排查了半天才发现是这两项对不上。
mks.maxRefreshRate限制客户机的刷新率上限。默认值在某些高刷屏上会导致 CPU 占用异常,设成 60 是比较保守稳妥的做法,追求高刷可以设 120 试试。
改完之后保存,启动虚拟机,进系统验证。
2.3 Ubuntu 客户机:驱动栈与验证方法
客户机侧最常犯的错误是只装了open-vm-tools,没装open-vm-tools-desktop。前者是基础服务,后者才包含图形相关的组件,包括分辨率自适应、剪贴板共享、3D 加速的客户机侧支持。
sudo apt update sudo apt install -y open-vm-tools open-vm-tools-desktop mesa-utilsmesa-utils里包含glxinfo和glxgears,是验证的核心工具。装完重启一次,让vmwgfx内核模块重新加载。
重启后先确认内核模块:
lsmod | grep vmwgfx有输出说明模块加载正常。如果为空,检查是不是把nomodeset加到内核参数里了——这个参数会禁用内核态显卡驱动,vmwgfx起不来,3D 加速必然失效。
然后验证 OpenGL:
glxinfo -B重点看三行:
OpenGL renderer string:正常应该是SVGA3D开头的字符串,例如SVGA3D; build: RELEASE; LLVM;。如果显示llvmpipe (LLVM xx.x, 256 bits),说明走的是 CPU 软件渲染,3D 加速没生效。OpenGL version string:这里能看到客户机支持的 OpenGL 版本上限,通常和虚拟硬件版本、Mesa 版本有关。Device:显示的是虚拟显卡标识。
如果是 Wayland 会话,glxinfo可能会连到 XWayland 上,读出来的信息不一定准。先确认会话类型:
echo $XDG_SESSION_TYPE如果是wayland而glxinfo结果异常,可以注销后在登录界面右下角的齿轮里切回Ubuntu on Xorg再测。Xorg 会话下glxinfo的结果更能反映真实状态。
想更直观地看性能,装一个glmark2:
sudo apt install -y glmark2 glmark2跑完会给出一个总分。这个分数不用跟网上的数据比,因为差异太大,它的价值在于你自己机器的前后对比:改参数前跑一次,改完再跑一次,看有没有提升。
2.4 实测表现与能力边界
我在一台配备中端独显的机器上做过一组对比,宿主机是 Windows,Workstation 版本较新,Ubuntu 22.04 客户机,配置 4 核 8G 内存。同一套glmark2,结果大致如下:
| 配置状态 | glmark2 总分 | glxinfo 渲染器 | CPU 占用 |
|---|---|---|---|
| 未开 3D 加速 | 约 300 | llvmpipe | 极高,跑分时占满两核 |
| 开 3D 加速,显存 256MB | 约 1500 | SVGA3D | 中等 |
| 开 3D 加速,显存 512MB,4K 上限 | 约 1800 | SVGA3D | 中等 |
| 同硬件实体机原生 | 约 8000+ | 独显型号 | 低 |
这组数据里最值得注意的是最后一行的对比。即便配置调到最优,虚拟机的图形性能也只有实体机的两成出头。这个差距不是配置问题,是转发架构本身的代价——每一条 OpenGL 命令都要序列化、跨虚拟机边界传输、在宿主机上重放,链路越长损耗越大。
所以方案一的适用边界很清晰:
够用的场景:Ubuntu 桌面日常操作、浏览器多标签、看高清视频、跑轻量级的 Qt 或 GTK 应用、做界面预览、跑一些对帧率不敏感的可视化脚本、教学演示。
不够用的场景:三维建模的实时预览(拖个模型就掉到个位数帧)、游戏、视频剪辑的时间线预览、任何需要 CUDA 或 OpenCL 的任务、任何需要 GPU 硬件编解码的任务。
实操心得:有个细节很多人不知道,虚拟机窗口的尺寸会直接影响渲染性能。客户机分辨率和虚拟机窗口尺寸不匹配时,MKS 需要做一次额外的缩放,这一层缩放是纯 CPU 干的,帧率会掉得很明显。把客户机分辨率设成和窗口大小一致,或者反过来让窗口自动适应,能省下不少性能。
另外提一句,开启 3D 加速之后如果发现客户机启动变慢或者宿主机 CPU 占用异常,可以先把svga.vramSize往回调小一档试试,显存分配过大在某些宿主机上会触发内存压缩,反而拖慢整体。
3. 方案二:PCIe 直通,把整块显卡交给 Ubuntu
如果你读到这一章,说明你的需求是第三层——要 CUDA、要真显存、要原厂驱动。那么第一句话必须说清楚:VMware Workstation 做不到这件事。它的产品定位是桌面级虚拟化,图形部分只提供命令转发的虚拟显卡,没有实现 PCIe 设备的直接分配。
网上有些文章会提到“VMDirectPath”这个词,那是 ESXi 的功能,而且需要在 BIOS 里开启 VT-d、在 ESXi 的“管理 > 硬件 > PCI 设备”里勾选直通,跟 Workstation 是两个产品线。Workstation 的虚拟机设置里翻遍也找不到直通选项,不是版本问题,是没这个功能。
所以要实现真正的直通,路线是换平台。工程上用得多的是 KVM/QEMU 配合 VFIO 框架,成熟、文档多、可控性强;如果偏好图形化界面,基于同一套内核能力的虚拟化管理平台也可以,本质一样。下面按 KVM 这条线讲,因为它最能说明每一层发生了什么。
3.1 更换平台前的硬件与固件检查清单
这一步跳过了,后面一定会在某个莫名其妙的地方卡住。我把检查项整理成清单,一条条过。
CPU 虚拟化与 IOMMU 支持。Intel 平台需要 VT-x 加 VT-d,AMD 平台需要 AMD-V 加 AMD-Vi。查一下:
lscpu | grep -i -E "virtualization|vt-d|iommu"输出里有VT-x或AMD-V说明基础虚拟化有,但 VT-d/IOMMU 不一定会在这里显示,更可靠的是查主板手册或者看 BIOS 里有没有对应开关。
BIOS/UEFI 设置。需要打开三个东西:虚拟化技术(Intel VT-x / SVM Mode)、I/O 虚拟化(Intel VT-d / IOMMU)、Above 4G Decoding。第三项很多人漏掉,它决定了系统能不能给 PCIe 设备分配 64 位地址空间,不打开的话大显存的卡(16G 以上)直通时容易出问题。
显卡数量。这是最硬的门槛。直通之后这块卡就归虚拟机独占了,宿主机完全用不了它。所以宿主机必须还有另一块显卡负责输出画面——可以是 CPU 核显,也可以是一块便宜的亮机卡。如果只有一块独显,那就变成“单卡直通”,需要在启动时决定把卡给谁,配置复杂度和翻车率都高一个档次。
IOMMU 分组。这个后面单独讲,但先说结论:理想情况下目标显卡和它的音频功能单独成组,组的边界干净。如果不干净,就得考虑 ACS 覆盖补丁,而那个补丁有安全代价。
电源和散热。直通状态下 GPU 长时间高负载,机箱风道和电源余量要留够。听起来像废话,但我确实见过因为电源功率不足导致直通后跑满载就重启的情况。
3.2 IOMMU 分组:直通成败的关键
IOMMU 分组是直通里最容易出问题的一环。简单说,IOMMU 把 PCIe 设备按“能不能被独立隔离”分成若干组,直通的最小单位是组,不是设备。也就是说,你要直通的那块显卡所在的组里,所有设备必须一起交给虚拟机,否则内核会拒绝。
查看分组的脚本:
#!/bin/bash shopt -s nullglob for g in /sys/kernel/iommu_groups/*; do echo "IOMMU Group ${g##*/}:" for d in $g/devices/*; do echo -e "\t$(lspci -nns ${d##*/})" done done把输出保存下来慢慢看。理想的结果是这样的:
IOMMU Group 14: 01:00.0 VGA compatible controller [0300]: ... 01:00.1 Audio device [0403]: ...显卡和它的 HDMI 音频功能在一个组里,组里没有别的东西。这是最干净的形态。
不理想的情况是显卡和一堆 PCIe 桥、网卡、SATA 控制器挤在一个组里,原因通常是主板 PCIe 插槽的 ACS(访问控制服务)能力不足,或者芯片组的拓扑结构限制了隔离粒度。这时候有几个应对思路:
第一,换插槽。不同插槽挂在不同 PCIe 根端口下,分组结果可能完全不同。把显卡从 CPU 直连的插槽换到芯片组提供的插槽,或者反过来,都值得试一次。这是成本最低的办法。
第二,BIOS 里找 ACS 相关选项。部分主板有“PCIe ACS Override”之类的开关,打开能改善分组,但这属于厂商自行实现的功能,效果因板而异。
第三,ACS 覆盖补丁。内核提供pcie_acs_override参数,可以强行把设备拆成独立组。代价是破坏了设备间的隔离性,理论上存在 DMA 攻击面扩大的风险,家庭实验室自己用问题不大,生产环境慎用。
第四,接受现实换硬件。如果主板的分组实在很难看,换一块支持更好的板子往往比折腾一周更省事。
注意:判断分组好坏有个经验法则——如果显卡所在组里的设备数量超过 3 个,就要警惕了。组里每多一个设备,直通时需要一起绑定的东西就多一份,而其中任何一个设备(比如你的系统盘控制器)都不太可能被交出去。
3.3 内核参数与 vfio-pci 绑定
分组干净之后,进入配置阶段。整体思路是:让内核启动时就开启 IOMMU,然后在显卡驱动加载之前把目标设备抢过来绑定到vfio-pci,这样宿主机就再也不会去碰它。
第一步,改 GRUB 内核参数:
sudo nano /etc/default/grub找到GRUB_CMDLINE_LINUX_DEFAULT,Intel 平台改成:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt"AMD 平台改成:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt"iommu=pt的意思是“只对需要直通的设备启用 IOMMU 转换”,对没有直通的设备走直通模式,能减少一点性能开销。改完更新 GRUB:
sudo update-grub重启后验证 IOMMU 是否真的开了:
dmesg | grep -i -E "DMAR|IOMMU"看到DMAR: IOMMU enabled或AMD-Vi: Interrupt remapping enabled之类的输出就对了。如果什么都没看到,说明 BIOS 里那个开关没打开,回去检查。
第二步,获取设备的 PCI ID:
lspci -nn | grep -i vga lspci -nn | grep -i audio输出形如01:00.0 VGA compatible controller [0300]: ... [10de:2489] (rev a1),方括号里的10de:2489就是厂商 ID 加设备 ID。
第三步,创建绑定配置:
sudo nano /etc/modprobe.d/vfio.conf内容:
options vfio-pci ids=10de:2489,10de:228b softdep nvidia pre: vfio-pci softdep nouveau pre: vfio-pciids那里填上一步拿到的两个 ID,用逗号分隔,显卡本体和它的音频功能都要写上,不写音频的话虚拟机会出现“有画面但没声音”的诡异状态。
softdep两行是关键。它告诉内核在加载nvidia或nouveau驱动之前先加载vfio-pci,这样 vfio 会先一步抢到设备,原厂驱动再去枚举时就看不到这张卡了,自然不会来抢。少了这两行,大概率出现设备已经被 nvidia 驱动接管、vfio 绑不上的情况。
第四步,把 vfio 模块写进 initramfs,让它在启动早期就可用:
echo "vfio" | sudo tee -a /etc/initramfs-tools/modules echo "vfio_iommu_type1" | sudo tee -a /etc/initramfs-tools/modules echo "vfio_pci" | sudo tee -a /etc/initramfs-tools/modules echo "vfio_virqfd" | sudo tee -a /etc/initramfs-tools/modules sudo update-initramfs -u -k all第五步,重启,然后验证绑定结果:
lspci -nnk -d 10de:看目标设备那一行的Kernel driver in use,如果是vfio-pci,恭喜,绑定成功。如果显示nvidia或nouveau,说明softdep没起作用或者 initramfs 没更新干净,重新走一遍第四步。
3.4 虚拟机侧配置与客户机验证
在虚拟化管理工具里给虚拟机添加 PCI 主机设备时,本质上是往虚拟机配置里写一段<hostdev>。手写的话长这样:
<hostdev mode='subsystem' type='pci' managed='yes'> <source> <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> </source> <rom bar='on'/> <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/> </hostdev>source里填的是宿主机上那张卡的真实总线地址(从lspci里读出来的 01:00.0),address里填的是它在虚拟机内部被映射到的位置,这个可以随便指定一个空闲位置,但要在虚拟机的 PCI 拓扑里合法。
<rom bar='on'/>这一行是给显卡暴露 vBIOS ROM 的。有些消费级显卡在直通时因为 vBIOS 不能被正确读取而初始化失败,加上这一行通常能解决。如果还是不行,可能需要从显卡里 dump 出 vBIOS 文件然后在配置里指定file=路径,那是另一个层级的操作了。
虚拟机配置里还有一个必须注意的点:芯片组和固件类型。用 UEFI(OVMF)固件比传统 BIOS 更容易让现代显卡正常初始化,而且在 UEFI 下显卡的 GOP 驱动能正常跑,开机画面能出来。如果用传统 BIOS 固件,很多新卡直通后会黑屏但系统实际在跑,只能靠 SSH 操作。
客户机启动后,先在lspci里确认真实显卡出现了:
lspci -nn | grep -i nvidia然后装原厂驱动。Ubuntu 上最省事的是ubuntu-drivers工具:
sudo ubuntu-drivers devices sudo ubuntu-drivers autoinstall sudo reboot重启后验证:
nvidia-smi正常输出会列出显卡型号、显存大小、驱动版本、当前温度功耗。这一行输出出现的那一刻,说明整条链路彻底打通了。之后装 CUDA Toolkit、装 PyTorch、跑torch.cuda.is_available(),都是一路顺风。
3.5 性能损耗实测与几个必须知道的代价
直通的性能表现是我最常被问到的第二个问题。我在几套配置上跑过对比,结论是:计算几乎无损,图形有轻微损耗,延迟略有增加。
| 测试项 | 实体机原生 | 直通虚拟机 | 损耗 |
|---|---|---|---|
| CUDA 带宽测试 | 100% | 96%~98% | 2%~4% |
| 矩阵乘法算力 | 100% | 95%~97% | 3%~5% |
| 图形渲染帧率 | 100% | 90%~95% | 5%~10% |
| 显存访问延迟 | 100% | 略高 | 可感知但不明显 |
| 视频硬编码速度 | 100% | 接近原生 | 很低 |
计算损耗主要来自 IOMMU 地址转换和中断重映射,这部分开销是固定的,跟负载大小关系不大,所以跑大任务时占比可以忽略。图形损耗除了地址转换,还多了一层帧缓冲的搬运,所以比计算略高。
代价方面,有几个必须提前接受:
宿主机失去这块卡。直通期间宿主机完全用不了它,包括跑 CUDA、跑推理、玩游戏。如果宿主机平时也需要用显卡,就得在“给虚拟机”和“给宿主”之间做取舍,或者配置动态绑定(启动虚拟机时解绑、关闭时归还),但那又是另一套复杂度。
功耗和发热。直通状态下 GPU 无法被宿主机的电源管理降频,空闲时也可能保持较高功耗。笔记本上尤其明显,风扇会一直转。
快照和迁移受限。带直通设备的虚拟机通常不能做内存快照,也不能在线迁移到别的宿主机。做实验时要养成关机前保存状态的习惯。
内核升级后可能失效。这是最容易被忽略的一条。内核大版本升级后,模块加载顺序、initramfs 内容都可能变化,出现过重启后vfio-pci没绑上的情况。建议每次内核升级后跑一遍lspci -nnk -d 10de:确认绑定状态。
4. 折中路线:把宿主机当成 GPU 服务端
前两章是两个极端:一个是完全虚拟化、性能打折但简单,一个是完全直通、性能接近原生但代价大。实际工作中还有第三条路,很多人没想到,但在我做的项目里用得最多——不把 GPU 交给虚拟机,而是把计算任务送到宿主机执行。
这个思路的核心是:Ubuntu 虚拟机里跑的往往不是“需要 GPU 的完整桌面”,而是“需要 GPU 的某个任务”。既然是任务,就可以通过网络或进程间通信把它扔出去,算完了把结果拿回来。虚拟机只负责编辑代码、跑前端、做数据分析,重活全在宿主机上干。
4.1 三种落地方式与各自适用场景
第一种:远程执行命令。最土但最可靠。虚拟机里通过 SSH 连到宿主机,把训练脚本、渲染任务提交过去,输出文件通过共享目录或者scp拿回来。
ssh user@host-ip "cd /work/project && CUDA_VISIBLE_DEVICES=0 python train.py --config cfg.yaml"这种方式的好处是零额外组件,宿主机上装好驱动和运行环境就行,虚拟机里连 CUDA 都不用装。缺点是交互性差,调试不方便,适合“提交-等待-取结果”的批处理型任务。
第二种:网络化服务。在宿主机上起一个常驻服务,虚拟机作为客户端调用。常见形态有几种:Jupyter Notebook 服务端跑在宿主机,虚拟机里用浏览器连过去写代码;推理服务用 HTTP 或 gRPC 暴露接口,虚拟机里当普通 API 调;渲染集群用任务队列分发,虚拟机只提交任务描述。
import requests resp = requests.post( "http://host-ip:8000/infer", json={"prompt": "a cat sitting on a keyboard", "steps": 30}, timeout=120, ) print(resp.json()["image_base64"][:64])这种方式的优势是把 GPU 资源变成了“服务”,多个虚拟机、多个项目可以共享同一块卡,按需排队。代价是要额外维护服务端,接口设计要考虑并发和超时。
第三种:共享内存加本地套接字。如果虚拟机和宿主机之间有共享文件夹或者 VirtIO 的高速通道,可以用本地套接字传递数据,省掉网络协议栈的开销。这种方式配置最麻烦,收益在数据量特别大(比如视频帧流)的时候才明显,一般项目用不上。
4.2 三条路线的选择依据
| 判断维度 | 方案一:内置 3D 加速 | 方案二:PCIe 直通 | 折中:GPU 服务端 |
|---|---|---|---|
| 主要用途 | 图形界面、轻量渲染 | 完整 GPU 能力 | 批处理、推理、训练 |
| 交互体验 | 好 | 好 | 一般 |
| 调试便利性 | 好 | 好 | 较差 |
| 多虚拟机共享 | 天然支持 | 不支持 | 天然支持 |
| 配置成本 | 低 | 高 | 中 |
| 适合人群 | 办公、教学、演示 | 单机深度学习、图形工作站 | 团队共享、多项目并行 |
我的实际选择逻辑是这样的:如果只是想在虚拟机里舒服地用 Ubuntu 桌面,走方案一,顺手把 3D 加速开了就行;如果这台机器就是给某个深度学习项目专用的,走方案二,一次性投入换长期收益;如果是团队环境,好几个人要用同一块卡,那折中路线才是正解,直通反而是浪费。
实操心得:折中路线上有个细节值得注意——宿主机和虚拟机的文件系统要打通。最省事的做法是虚拟机里挂载宿主机的共享目录,代码在宿主机上,虚拟机里直接编辑,跑的时候在宿主机执行。这样避免了“改了代码忘了同步”这类低级错误。数据量大就用 NFS 或者干脆把数据集放宿主机上,虚拟机只读路径。
5. 常见问题与排查实录
这一章是我这些年被问到最多的问题的汇总,按现象归类,方便对照排查。
5.1 现象速查表
| 现象 | 最可能的原因 | 处理方向 |
|---|---|---|
glxinfo显示 llvmpipe | 3D 加速没生效 | 检查 .vmx 的mks.enable3d,检查宿主机驱动 |
| 虚拟机里分辨率上不去 | svga.maxWidth/Height太小 | 调到目标分辨率以上 |
| 挂起恢复后黑屏 | vmotion.checkpointFBSize与显存不一致 | 两项设成相同值 |
| 开了加速反而更卡 | 虚拟显存分配过大触发内存压缩 | 把svga.vramSize降一档 |
| 直通后虚拟机启动黑屏 | vBIOS 未正确暴露 | 配置里加<rom bar='on'/>或用 UEFI 固件 |
lspci看不到直通卡 | vfio 绑定失败 | 检查softdep和 initramfs |
nvidia-smi报驱动未加载 | 客户机没装驱动或装错版本 | 用ubuntu-drivers重装 |
| 直通后宿主机重启进不去系统 | 显卡被 vfio 抢走后宿主无输出 | 用核显输出或临时移除绑定参数 |
| 内核升级后直通失效 | 模块顺序变化 | 重新update-initramfs并验证绑定 |
5.2 五个我实际踩过的坑
坑一:只改了.vmx没关虚拟机。Workstation 在虚拟机运行时会锁定.vmx文件,你的修改要么被忽略,要么在关机时被覆盖回去。改之前一定完整关机,不是挂起。
坑二:宿主机驱动在黑名单里。这个最隐蔽,因为宿主机自己跑得好好的,glxinfo也正常,就是虚拟机里不加速。加mks.gl.allowBlacklistedDrivers = "TRUE"之后立刻好了。原因是 Workstation 对某些驱动版本有兼容性判断,判断失准时会保守地退回软件渲染。
坑三:直通时只绑了显卡没绑音频。表现是虚拟机里画面正常但aplay -l找不到 HDMI 输出设备。查lspci会发现音频功能还挂在宿主机上。解决办法是把同一组的音频设备 ID 也加进vfio.conf的ids里。
坑四:Secure Boot 拦住了第三方模块。开启 Secure Boot 的系统上,vfio-pci这类模块如果没签名会被拒绝加载,dmesg里能看到module verification failed之类的信息。要么在 BIOS 里关掉 Secure Boot,要么给模块签名,前者简单得多。
坑五:直通后宿主机功耗飙升。这张卡在宿主机上时会被电源管理降频,直通给虚拟机后客户机驱动接管,空闲时也保持在较高功耗状态。解决办法是在客户机里配置持久模式或者在不用的时候关掉虚拟机。笔记本用户对这一条体会最深,风扇声会明显变大。
5.3 一套通用的排查顺序
遇到问题别乱试,按这个顺序走一遍,八成能定位:
第一步,确认需求层次。先搞清楚是图形问题还是计算问题,这决定了往哪个方向查。图形问题查 MKS 和 Mesa,计算问题查 vfio 和驱动。
第二步,逐层验证。方案一从宿主机驱动开始,到.vmx参数,到客户机模块,每一层都用命令验证一遍,不要凭感觉。方案二从 BIOS 开关开始,到内核参数,到 IOMMU 分组,到 vfio 绑定,每一层都有对应的验证命令。
第三步,看日志而不是猜。dmesg、journalctl -xe、/var/log/vmware/下面的日志,信息量比任何猜测都大。我见过太多人凭“应该是驱动问题”这个判断折腾半天,结果日志里明明白白写着绑定失败的原因。
第四步,最小化复现。如果配置改了很多版都失败,就退回到最简配置:只开mks.enable3d,别的参数全删,看能不能跑通,再一项项加回来。直通也一样,先只绑显卡不绑音频,跑通了再加。
我个人在这些年的操作里最深的体会是,虚拟机用 GPU 这件事,方向选对比参数调优重要一百倍。想清楚自己要的是哪一层能力,再决定花十分钟还是花两天,比闷头改配置高效得多。手上这台机器我刚配好直通那阵子,为了一个分组问题换了三次插槽,最后还是靠换主板解决的——但那是因为我明确知道必须要 CUDA,如果你的需求只是桌面流畅一点,真没必要走到这一步。