
1. 故障现场双卡环境里 CUDA 突然“半瘫”是什么体验先说结论写这篇文章不是为了复述一个“哦我修好了”的故事而是这次排错链路真的太典型了——单卡掉卡 → 怀疑显卡欺骗器 → 降级 WSL 内核 → 最后发现是 NVIDIA 驱动版本问题每一环单独拎出来都有人写过但很少有人把它们串起来讲。如果你也在 WSL2 里跑 CUDA而且是双 NVIDIA GPU 的机器建议你花十分钟把这篇文章看完省得后面走弯路。我的工作环境是一台双 GPU 工作站一张 RTX 4090 负责显示输出和日常桌面另一张 A6000 专门跑 CUDA 训练任务。系统是 Windows 11 WSL2WSL 里装的是 Ubuntu 22.04平时深度学习的活基本都在 WSL 里做。整体配置跑了小半年一直很稳。但某天我启动一个 PyTorch 训练任务时突然发现日志里频繁出现CUDA error: all CUDA-capable devices are busy or unavailable接着 nvidia-smi 直接告诉你只有一张卡另一张卡像人间蒸发一样消失了。这种间歇性掉卡最磨人你说它彻底坏了吧重启一次又能看见你说它没问题吧训练到中途就崩。更要命的是它不报硬错误不蓝屏不触发 WSL 崩溃就是 GPU 在 nvidia-smi 里进进出出跟闹鬼似的。因为工作需要长期跑模型这种随机掉卡对实验进度的打击是毁灭性的——不仅训练中断中间产生的 checkpoint 状态还会让你误以为“模型没收敛”实则是计算中途被打断了。你如果也遇到过类似问题多半能从下面的排查记录里找到对应阶段。整个定位过程前后花了两三天中间试了硬件重插、显卡欺骗器排查、WSL 内核降级最后才锁定驱动版本本身。我尽量按排查的时间线来写每步都说明为什么这么做、实测结果如何、当时是怎么排除的这样你下次遇到类似问题能直接照着思路走。2. 第一阶段排查单卡掉卡先别急着怀疑硬件先说掉卡的直接表现。我当时在 WSL2 里执行nvidia-smi输出里只有一张卡再执行一次偶尔又变回两张。这种“时好时坏”的状态最容易被误判成硬件接触不良。很多人第一步就是拆机箱重新插拔显卡我也这么干了结果毫无变化两张卡的金手指都擦过了供电线也重新插过一遍。老实说这一步意义不大——如果是物理接触问题掉卡表现不会是“间歇性偶尔恢复”而是一旦松动就一直认不到。接下来我在 Windows 宿主机侧做了检查。在设备管理器里看两张卡的设备状态一切正常无感叹号、无错误代码 43、无资源冲突。然后在 Windows PowerShell 里跑nvidia-smi -L两张卡都能正确列出。这说明硬件层面和 Windows 驱动层面都没问题问题出在 WSL2 侧对 GPU 的枚举或接待流程上。这里需要补充一个 WSL2 的底层机制。WSL2 不是虚拟机里直接分一个 PCIe 设备给 Linux而是通过 GPU-PVGPU 半虚拟化技术把 Windows 侧 WDDM 驱动的能力映射到 Linux 侧。你在 WSL 里看到的/dev/dxg设备本质上是 Windows 图形内核的一个“代理”。所以 WSL 里 nvidia-smi 看到几张卡很大程度上取决于 Windows 侧 WDDM 模式能否正常枚举到 GPU以及 Linux 侧/usr/lib/wsl/lib/nvidia-smi这个转发工具能否正确读到 Windows 侧的报告。当时我重点怀疑的问题是双卡到底是不是处在同一个驱动模式下。NVIDIA 驱动在 Windows 下有两种模式WDDM 和 TCC。TCC 模式下 GPU 不做显示输出主要面向计算枚举更稳定WDDM 模式则带图形调度器会接管显示输出。如果两张卡一台机器里一个 WDDM、一个 TCC 混用在某些驱动版本下确实容易出怪问题。我执行了nvidia-smi -g 1 -dm 1想把 A6000 切到 TCC 模式结果命令也执行成功了但问题依旧。这条线索到这里暂时搁置后来回头看它本身就是驱动层面对多卡管理异常的一种征兆。紧接着我在 WSL2 里检查了/proc/driver/nvidia/gpus目录列表里只有一张卡的 PCI 信息另一张完全没有。这说明掉卡现象发生在 WSL 内核的 GPU 枚举层——Linux 侧压根没有认到第二张卡的 PCIe 设备而不是 CUDA Runtime 层面的“设备忙”。我顺手看了下dmesg | grep -i nvidia发现了大量关于设备映射失败的记录错误码指向 WSL 的 VM 总线传输层。这一步基本把嫌疑从硬件转移到了虚拟化层和驱动层。3. 显卡欺骗器的介入与排除看起来可疑其实不是真凶关于“显卡欺骗器”这一段得先解释一下这个名词因为不少朋友看到这里会一头雾水。显卡欺骗器EDID 欺骗器 / HDMI dummy plug是一个物理小插头插在显卡的 HDMI 或 DP 口上给显卡一个“有显示器在线”的假信号。它最早是无头服务器、矿机托管场景用的——显卡没有接显示器时有些驱动和应用会认为该 GPU 不可用或者对显卡频率、显存频率做降级处理插上欺骗器后显卡就会以满负载状态工作还能解决一些远程桌面黑屏的兼容问题。我当时的工作站是常年无显示器运行为了远程桌面方便在 A6000 上插了一个 HDMI 欺骗器。排查单卡掉卡时我把目光落在它上面不是没有道理的。WSL2 的 GPU 转发依赖 WDDM 图形调度器而 WDDM 调度器对“有显示输出任务的 GPU”和“纯计算 GPU”的处理逻辑不同。如果有显示器/欺骗器附在某一颗 GPU 上Windows 会把它视为“主显示设备”加一分负载和调度上的不稳定性再加上当时双卡恰好一卡负责桌面、一卡插着欺骗器我完全有理由怀疑那颗挂着欺骗器的卡是不是因为 WDDM 调度器的某种状态竞争导致在 WSL 侧偶发枚举失败。于是我做了一个控制变量实验把 A6000 上的 HDMI 欺骗器拔掉彻底断掉它和任何显示信号的关联然后重启 WSL观察掉卡是否停止。测了半天结果令人失望——掉卡依旧随机出现甚至拔掉欺骗器后第一分钟就复现了。我再把欺骗器插回原来的 RTX 4090 上现象也没有变得更严重。这说明欺骗器跟掉卡没有直接因果关系。但这个环节也不是完全没有收获。拔掉欺骗器之后我观察到 Windows 侧 GPU 的“假负载”变低了——因为之前插着欺骗器时Windows 会真的把一个桌面画面“渲染”给不存在的显示器白白吃掉一小部分 GPU 资源还会让驱动在轮询显示状态时多出大量中断。如果你在无头工作站上插了欺骗器跑 CUDA 任务时应该知道这一点它会占用一点算力尤其是在 Remote Desktop 开启时。我把欺骗器从 A6000 拔掉后至少让那颗计算卡彻底从“显示调度”中解脱。排除了欺骗器之后焦点又重新回到系统软件栈。这个时候我做了个关键测试在 Windows 侧跑 CUDA 样本程序看双卡是否稳定。结论是——Windows 侧一切正常双卡都能被 CUDA 识别训练跑几个小时不出错。也就是说问题高度集中在 “WSL2 → GPU 转发 → Linux 侧 CUDA” 这一段链路。4. WSL 内核降级一次有根据的尝试但只解决了表象到了这一步嫌疑指向两个方向WSL2 内核版本或 NVIDIA 驱动版本。我先选择了 WSL2 内核。理由很直接当时的 WSL2 内核是从 Microsoft Store 自动更新到 6.6 系列的而 6.6 内核引入了一些和 GPU 半虚拟化相关的改动。NVIDIA 官方对 WSL 的驱动支持主要面向 Linux 侧内核模块WSL 内核版本太新或太旧都可能和 Windows 侧驱动不完全兼容。另外我之前排错时dmesg里的 VM 总线传输错误也可能与内核更新后设备映射逻辑改变有关。WSL2 内核降级的操作不复杂但细节不少。先说怎么看当前 WSL 内核版本uname -r我当时的输出大概是6.6.xx.x-microsoft-standard-WSL2。要换回旧版内核比如 5.10 或 5.15需要去 WSL2 内核的 GitHub 发布页下载对应的kernel文件然后替换掉 Windows 侧的kernel文件。这里有个容易踩坑的点现在的 WSL2 默认从 Microsoft Store 分发内核文件路径不再是老的C:\Windows\System32\lxss\tools\kernel而是位于C:\Program Files\WSL\tools\kernel或通过wsl --update --web-download管理的位置。如果你用旧教程找路径很容易找不到或改错文件。我是这样操作的先备份当前内核cp /mnt/c/Program\ Files/WSL/tools/kernel /mnt/c/Users/你的用户名/backup_kernel从 GitHub 发布页下载指定版本的kernel文件注意选kernel而不是kernel.zip后者是带符号的包备份好原内核后把下载的新内核命名为kernel并替换到对应目录在 Windows 侧执行wsl --shutdown再重启 WSL 让新内核生效在 WSL 里再执行uname -r确认版本切换我当时把内核从 6.6 降到 5.15 LTS 版本。降级后的效果是掉卡频率明显下降从“几小时一次”变成“一两天一次”不是彻底消失。这说明内核版本确实参与了这个问题的暴露但严格意义上它只是个放大因素不是根因。这里值得展开说一句为什么 WSL 内核版本会影响 GPU 掉卡因为 WSL2 的 GPU 共享依赖dxgkrnl驱动模块这个模块在内核态负责把 Linux 侧的 GPU 请求转发给 Windows 侧的 WDDM 调度器。不同内核版本里dxgkrnl的竞态处理和超时机制不一样。如果它内部的锁竞争触发超时就会出现设备映射丢失的现象。降级到老内核不一定是老内核没 bug而是老内核的调度行为更“迟钝”不容易触发竞态新内核更激进调度频率更高暴露问题的概率就大。也就是说降内核只是把症状压住了没能除根。同时我还在.wslconfig里做了一处调整把内存和 CPU 设置改保守了一点[wsl2] memory32GB processors8这个调整的目的不是解决掉卡而是为了排除“资源竞争导致 GPU 枚举超时”这条假设。WSL2 的资源限制和宿主机共享之间不是硬隔离如果内存压力大可能在 GPU 映射时出现不可预期的行为。实测调整后没有任何改善这条也算排除了。5. 最终定位把矛头指向 NVIDIA 驱动版本本身排除了欺骗器、排除了 WSL 内核Windows 侧又完全正常那剩下的最大嫌疑就只剩一个东西NVIDIA 驱动版本与 WSL2 双卡的兼容性。这个地方我想多讲一点排查手法因为它比最终结论本身更有参考价值。第一个有力的证据来自驱动版本号对比。你在 WSL2 里执行nvidia-smi顶部显示的 Driver Version 其实不是 Linux 侧驱动的真实版本而是 Windows 侧驱动通过转发工具上报的版本。当时我 WSL 里看到的是551.86Windows 侧 PowerShell 里看到的也是551.86。表面上一模一样但问题在于这个驱动版本和我的 CUDA Toolkit 版本是否真的在兼容矩阵里我当时在 WSL 里用的 CUDA 是 12.4PyTorch 2.4。按 NVIDIA 官方兼容性表CUDA 12.4 要求驱动最低550.54.14。而我的551.86是高于这个门槛的理论上没毛病。但注意——“高于门槛”不等于“等于或被测试过的最佳组合”。NVIDIA 的测试矩阵里针对 WSL2 双卡场景的回归测试往往不如单卡全面一些驱动版本在双卡 WSL2 的组合下就是存在已知或未知的间歇性问题。第二个证据出现在 Windows 事件查看器里。我打开“应用程序”日志过滤 NVIDIA Container 相关条目发现每次掉卡前都有几条来源为NVWMI或NVIDIA Container的警告事件内容大致是“GPU 0 上下文创建失败”或“GPU 1 从 TDR 恢复”。这说明在 Windows 侧驱动已经感知到了显卡异常甚至可能自己做了 TDRTimeout Detection and Recovery处理把卡重置了一遍。这进一步证明问题不在 WSL而在驱动层。第三个动作是验证性替换。我下载了另一版本的 NVIDIA 驱动具体做法是先卸载当前驱动建议用 DDU 在安全模式下卸载保证干净然后分别测试了新驱动和旧驱动。我选的对比对象是550.54.14这条分支——它更接近 CUDA 12.4 官方验证用驱动。卸载干净后装完重启进 WSL再跑双卡压力测试跑了整整一个下午掉卡一次都没出现。为了确认不是巧合我后来又换回551.86反向验证了一遍跑了两个小时掉卡立刻复现。到这里根因已经很明确当前这个 NVIDIA 驱动版本和 WSL2 双卡环境存在兼容性缺陷具体可能是在 WDDM 调度器对多 GPU 切换的处理上存在竞态触发条件不是每个驱动版本都会踩到。关于驱动替换这块有一个实操细节必须提醒不要在 WSL 里直接卸载或重装驱动。WSL2 下的 NVIDIA 驱动本质上就是 Windows 驱动你只需要在 Windows 侧把驱动装好WSL 侧重启后就会自动同步。如果卸载后 WSL 里nvidia-smi提示找不到命令不要慌不是 WSL 坏了只是驱动转发层暂时没建立重装好 Windows 驱动后一切就恢复了。6. 双卡 WSL2 环境的使用建议从这次排错里能带走什么这次排错花了三天最后发现就一个驱动版本不匹配的问题。但要是没有前面那些排查我可能直接重装系统了。复盘整个链路核心结论可以浓缩成下面几条。第一遇到 WSL2 里 GPU 掉卡先看 Windows 侧能不能稳定识别。这是最重要的分水岭。如果 Windows 侧nvidia-smi -L两条卡都在且跑 CUDA 样例也稳定那么问题 90% 出在 WSL2 转发层或驱动兼容性上跟硬件基本无关。不用急着拆机、插拔、换电源这些动作除了浪费时间还可能因为操作不当带来新故障。第二WSL 内核版本值得作为排查变量但不要指望换内核根治。我降级后从“几小时必现”变成“一两天偶现”看起来有效果但这只是降低了触发概率。判断一个解决方案是否真正生效最好做“反向验证”——装回原内核看问题是否复现否则容易被表面稳定误导。第三NVIDIA 驱动版本的选择在三类环境中优先级不同。单卡打游戏挑最新驱动没问题单卡跑 CUDA奔着新特性的可以选较新版本但WSL2 多卡 CUDA 这个组合一定要优先看 NVIDIA 官方 Release Notes 里对 WSL 支持的描述多参考社区反馈而不是无脑追新。我的建议是直接使用官方兼容矩阵中 CUDA 对应版本的最低推荐驱动分支这种分支往往经过了更多验证。第四显卡欺骗器在无头工作站上有用但要清楚它的副作用。如果你的 GPU 被欺骗器“伪装”成带显示器状态Windows 会真的给它分配一部分显示渲染任务这在 CUDA 任务里就是白白损失性能还可能在驱动调度上引入不确定性。如果 A6000 这种纯计算卡插着欺骗器建议拔掉如果是远程桌面的场景确实需要那就插在非主计算卡上并接受那一点性能损耗。最后再分享一个排错时的小技巧。整个排查过程中我坚持“每次只动一个变量”的原则。测欺骗器影响时只拔欺骗器测内核影响时只换内核测驱动影响时才动驱动。如果同时改两个变量出了问题你根本不知道是谁干的。这个原则听起来像废话但真到了焦头烂额的时候很多人就是会下意识“顺手把内存也拆下来擦一擦”“把 C 盘清理一下”——这些操作只会让你离真相越来越远。记录日志的习惯也很重要每次改动前把nvidia-smi输出、dmesg尾部、Windows 事件查看器里 NVIDIA 相关报错截图保存下来。回头看的时候你可能会在几页看似无关的日志里找到那个最初被你忽略的关键异常。