1. 为什么要在 Windows 上折腾内核级 Linux 加 GPU 直通
如果你是一个长期在 Windows 上做 AI 开发的从业者,大概率经历过这样的纠结:Windows 的桌面生态和办公软件确实方便,但一到跑模型训练、装 CUDA 工具链、配各种 Python 依赖的时候,就恨不得立刻切换到 Linux。以前常见的做法是装双系统,或者干脆再买一台机器专门跑 Linux。双系统的痛点是切换成本高,想查个资料还得重启;两台机器的痛点是文件同步麻烦,显卡还得买两张。
WSL2 的出现基本解决了这个矛盾。它不是早期 WSL1 那种"翻译层"式的兼容方案,而是微软在 Windows 内核之上跑了一个真正的 Linux 内核虚拟机,通过轻量级虚拟化技术实现。你在里面跑的是货真价实的 Linux 内核,系统调用是原生的,文件系统是 ext4,进程管理、网络栈都是标准 Linux 那一套。更关键的是,从 WSL2 开始,微软加入了 GPU 直通能力,也就是常说的 GPU paravirtualization(GPU-PV),让 Linux 子系统里的 CUDA 程序可以直接调用宿主机上的物理显卡。
这意味着什么?意味着你可以在 Windows 上开着 IDE 写代码,同时在 WSL2 里用nvidia-smi看到你的显卡,用 PyTorch 跑训练,性能损失在大多数场景下可以控制在个位数百分比。对于做深度学习、大模型微调、图像处理、科学计算的开发者来说,这套组合的性价比非常高。
不过,网上关于 WSL2 部署 AI 环境的文章大多停留在"装个 Ubuntu、装个 CUDA Toolkit"的层面,真正涉及到内核版本选择、GPU 直通验证、显存管理、多版本 CUDA 共存、性能调优这些深水区的内容并不多。我前后在三四台不同配置的机器上折腾过这套环境,踩过的坑包括驱动版本不匹配导致nvidia-smi报错、WSL 内核过旧识别不到显卡、CUDA 版本和 PyTorch 对不上、显存被 Windows 侧占用导致 OOM 等等。这篇文章就把这些经验系统性地整理出来,从底层原理到实操步骤,再到调优和排错,尽量让不同基础的读者都能照着走一遍。
适合阅读的人群:在 Windows 上做 AI 开发、想用 Linux 工具链但不想放弃 Windows 桌面的开发者;需要在本机跑 CUDA 程序但预算有限只有一张显卡的人;以及想搞清楚 WSL2 GPU 直通到底是怎么一回事的技术爱好者。
2. WSL2 的虚拟化架构与 GPU 直通到底是怎么实现的
2.1 WSL2 不是虚拟机软件,但确实用了虚拟化
很多人第一次听说 WSL2 是"基于 Hyper-V 的虚拟机"时会有点懵,因为它用起来完全不像传统虚拟机——没有开机画面,没有独立的网络配置界面,文件系统还能通过/mnt/c直接访问 Windows 盘符。这背后的设计思路是:微软把虚拟化做成了"隐形"的基础设施,用户感知不到虚拟机的存在,但底层确实跑着一个轻量级的 Utility VM。
具体来说,WSL2 使用的是一个经过高度裁剪的 Linux 内核,运行在 Hyper-V 的虚拟化层之上。这个内核由微软维护并开源,你可以通过wsl --update更新它,也可以自己编译内核替换。这个设计带来的一个直接好处是:内核版本可以独立于 Windows 系统更新,微软会定期把新的 Linux 内核特性合并进来。
对于 AI 开发来说,内核版本的重要性体现在几个方面。首先是 GPU 直通相关的驱动模块,比如dxgkrnl这个微软提供的内核态驱动,它负责在 Linux 侧和 Windows 的 GPU 驱动之间做桥接。如果内核太旧,这个模块可能不支持你的显卡架构,或者缺少某些必要的 ioctl 接口。其次是文件系统性能,WSL2 的 ext4 实现和 9p 文件共享协议在不同内核版本间有差异,直接影响数据加载速度。再就是一些新硬件(比如较新的网卡、USB 设备)的支持。
我个人的建议是:装完 WSL2 后第一件事就是wsl --update,把内核升到最新稳定版。截至我写这篇文章时,WSL2 内核已经到 5.15 以上的 LTS 分支,对主流 NVIDIA 显卡的支持都很完善。
2.2 GPU 直通的技术路径:从 DXG 到 CUDA
WSL2 的 GPU 直通不是传统意义上的 PCIe passthrough(那种需要 IOMMU 分组、把整张卡分配给虚拟机的方式),而是一种半虚拟化方案。整个链路大致是这样的:
Windows 侧的 GPU 驱动(比如 NVIDIA 的 Windows 驱动)负责实际管理硬件。WSL2 内核里有一个叫dxgkrnl的驱动,它通过 Hyper-V 的 VMBus 和 Windows 侧的 GPU 服务通信。当 Linux 里的 CUDA 程序要执行计算时,请求会经过dxgkrnl转发到 Windows 侧,由 Windows 驱动调度到 GPU 上执行,结果再原路返回。
这个链路听起来绕,但实际延迟增加很小,因为计算任务本身是批量的,通信开销被摊薄了。真正需要注意的是:Windows 侧的显卡驱动必须足够新,因为直通能力依赖驱动里实现的 WDDM(Windows Display Driver Model)接口。如果 Windows 驱动太旧,WSL2 里可能根本看不到显卡。
另一个关键点是:WSL2 里不需要、也不应该安装 NVIDIA 的 Linux 显卡驱动。你只需要在 WSL2 里装 CUDA Toolkit(它包含运行时和编译器),驱动部分由 Windows 侧提供。这一点和原生 Linux 完全不同,很多人第一次配的时候会习惯性地apt install nvidia-driver-xxx,结果把环境搞乱。
2.3 哪些显卡和场景支持 GPU 直通
不是所有显卡都能在 WSL2 里做 GPU 直通。基本要求是:
- NVIDIA 显卡:需要支持 WDDM 2.9 及以上,大致是 GTX 10 系及以后的消费级卡,以及对应的专业卡。驱动版本建议 470 以上,越新越好。
- AMD 显卡:需要较新的驱动,支持 ROCm 的卡在 WSL2 里的支持情况相对复杂一些,本文主要聚焦 NVIDIA 生态。
- Intel 核显:主要用于视频编解码和 OpenCL,做深度学习训练不太现实。
从场景上看,GPU 直通最适合的是:模型推理、中小规模训练、数据预处理加速、CUDA 加速的科学计算。如果你要做多卡分布式训练,WSL2 目前对多卡的支持有限,NCCL 在 WSL2 下的表现也不如原生 Linux,这种场景还是建议用原生 Linux 服务器。
3. 从零开始:WSL2 与 AI 工具链的完整落地步骤
3.1 启用 WSL2 与内核更新
第一步是在 Windows 上启用相关功能。以管理员身份打开 PowerShell,执行:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行完重启电脑。然后设置 WSL2 为默认版本:
wsl --set-default-version 2接着更新内核:
wsl --update这一步很关键,很多人跳过这步直接用系统自带的老内核,后面 GPU 识别不出来还找不到原因。更新完可以用wsl --version查看内核版本。
安装发行版,我一般选 Ubuntu 22.04 LTS 或 24.04 LTS,前者生态最成熟,后者内核更新。命令:
wsl --install -d Ubuntu-22.04装完会提示你设置 Linux 用户名和密码。这里有个小细节:用户名不要用中文,不要用大写字母,避免后面路径出问题。
3.2 Windows 侧驱动准备与验证
在装 CUDA Toolkit 之前,先确认 Windows 侧的 NVIDIA 驱动是最新的。打开 NVIDIA 控制面板或者用nvidia-smi(Windows 版)看一下驱动版本。建议 535 以上。
然后在 WSL2 里执行:
nvidia-smi如果能看到显卡信息,说明 GPU 直通已经通了。如果报command not found,说明 WSL2 里还没装 CUDA 工具链里的这个工具,但更可能的情况是驱动没通。可以先检查/dev/dxg这个设备节点是否存在:
ls -l /dev/dxg这个节点是 dxgkrnl 创建的,如果不存在,说明 GPU 直通没生效,需要回头检查 Windows 驱动和 WSL 内核版本。
3.3 CUDA Toolkit 的安装与版本选择
WSL2 里装 CUDA Toolkit 和原生 Linux 略有不同,因为不需要装驱动。NVIDIA 官方提供了 WSL-Ubuntu 的专用源。以 CUDA 12.x 为例:
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get -y install cuda-toolkit-12-4注意包名是cuda-toolkit-12-4而不是cuda,后者会尝试装驱动,在 WSL2 里会出问题。
版本选择上,我的经验是:跟着 PyTorch 的官方支持走。比如 PyTorch 2.4 官方支持 CUDA 12.1 和 12.4,那你就装对应的版本。不要盲目追最新,CUDA 13 刚出的时候很多库还没跟上,装了反而麻烦。
装完后配置环境变量,编辑~/.bashrc:
export PATH=/usr/local/cuda-12.4/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH然后source ~/.bashrc,再跑nvcc --version验证。
3.4 Python 环境与 PyTorch 安装
Python 环境我强烈建议用 conda 或 miniconda 管理,不要用系统自带的 Python。原因很简单:AI 项目依赖复杂,不同项目对 CUDA 版本、Python 版本要求不同,用 conda 可以隔离。
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh装完创建环境:
conda create -n ai python=3.11 conda activate ai然后装 PyTorch。去 PyTorch 官网查对应 CUDA 版本的安装命令,比如:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124装完验证:
import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果输出True和你的显卡型号,恭喜,环境通了。
4. 实测中那些让人抓狂的坑与排查链路
4.1 nvidia-smi 报错 "No devices were found"
这是最常见的问题。现象是 WSL2 里nvidia-smi能运行,但显示找不到设备。排查链路我总结成下面这个顺序:
先看 Windows 侧nvidia-smi是否正常。如果 Windows 侧都不正常,那是驱动问题,重装驱动。
再看 WSL 内核版本。uname -r看一下,如果是 5.10 以下的老内核,升级:wsl --update。
然后看/dev/dxg是否存在。不存在的话,检查 Windows 功能里"虚拟机平台"是否启用,以及 BIOS 里虚拟化是否打开。
还有一个容易被忽略的点:Windows 侧的显卡驱动如果是通过 Windows Update 自动装的,可能是精简版,缺少 WSL 直通需要的组件。这种情况去 NVIDIA 官网下完整版驱动重装。
我遇到过一次特别诡异的情况:/dev/dxg存在,Windows 侧驱动也正常,但 WSL2 里就是看不到卡。最后发现是 Windows 的"硬件加速 GPU 计划"(HAGS)和 WSL2 直通有冲突,在显示设置里关掉就好了。这个坑网上资料很少,我是折腾了大半天才定位到。
4.2 显存被 Windows 侧占用导致 OOM
WSL2 的 GPU 直通是共享显存的,Windows 桌面、浏览器、甚至一些后台程序都会占用显存。你有一张 8GB 的卡,Windows 侧可能就吃掉 1-2GB,WSL2 里实际可用只有 6GB 多。
排查方法:在 Windows 侧跑nvidia-smi看显存占用,把不必要的大户关掉。浏览器尤其是 Chrome,开几十个标签页能吃掉 1GB 以上显存。
如果做训练,建议在跑之前先算一下模型和 batch 的显存需求,留出余量。实在不够,可以考虑用 gradient checkpointing、混合精度这些技术降低显存占用。
4.3 WSL2 内存占用失控
WSL2 默认会占用宿主机最多 50% 的内存,而且不会主动释放。跑大模型的时候,Linux 侧的 page cache 会把内存吃满,Windows 侧就卡了。
解决办法是在 Windows 用户目录下创建.wslconfig文件:
[wsl2] memory=32GB processors=8 swap=16GB根据你机器的实际内存调整。改完wsl --shutdown重启生效。
这里有个经验:memory 不要设得太接近物理内存上限,给 Windows 侧留至少 8GB,否则系统会卡顿。swap 可以设大一点,但要注意 swap 在 SSD 上频繁读写会影响寿命。
4.4 文件系统性能:为什么你的数据加载这么慢
WSL2 访问 Windows 文件(/mnt/c/...)的性能很差,因为走的是 9p 协议,IO 延迟高。如果你把数据集放在 Windows 盘符下,训练时数据加载会成为瓶颈。
正确做法是把数据放在 WSL2 自己的文件系统里(~/下),或者用wsl --mount挂载物理磁盘。实测下来,同样的数据集,放在 ext4 里比放在/mnt/c里,DataLoader 的吞吐能差好几倍。
如果数据必须共享,可以考虑在 Windows 侧开个网络共享,WSL2 里通过 SMB 挂载,性能比 9p 好一些,但配置麻烦。最省事的还是直接复制到 WSL2 里。
5. 让环境跑得更稳:性能调优与日常维护经验
5.1 内核参数与 IO 调优
WSL2 的内核参数可以通过/etc/sysctl.conf调整。对 AI 开发比较有用的几个:
vm.swappiness=10 vm.dirty_ratio=15 vm.dirty_background_ratio=5降低 swappiness 可以减少不必要的 swap,提高 IO 响应。dirty ratio 调低可以让数据更及时地刷盘,避免突然的大量 IO 阻塞。
另外,如果你经常跑多进程数据加载,可以调大文件描述符限制:
ulimit -n 65535写进~/.bashrc里。
5.2 多版本 CUDA 共存的管理
实际项目里经常需要切换 CUDA 版本。我的做法是装多个版本的 toolkit,然后用环境变量切换。比如装了 12.1 和 12.4:
export CUDA_HOME=/usr/local/cuda-12.1 export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH可以写个简单的 shell 函数放在~/.bashrc里:
switch_cuda() { export CUDA_HOME=/usr/local/cuda-$1 export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH echo "Switched to CUDA $1" }用的时候switch_cuda 12.4就行。注意 PyTorch 这类库是编译时绑定 CUDA 版本的,切换 toolkit 不会改变已装 PyTorch 的 CUDA 版本,要重新装对应版本的 PyTorch。
5.3 备份与迁移:别等环境崩了才后悔
WSL2 环境折腾一次不容易,建议定期备份。命令很简单:
wsl --export Ubuntu-22.04 D:\backup\ubuntu-ai.tar恢复:
wsl --import Ubuntu-22.04 D:\wsl\ubuntu D:\backup\ubuntu-ai.tar导出的 tar 文件可能几十 GB,取决于你装了多少东西。建议在环境配好、还没装大数据集的时候先备份一份"干净版",后面玩坏了可以快速回滚。
还有一个技巧:把 WSL2 的虚拟磁盘文件(ext4.vhdx)移到非系统盘,避免 C 盘爆满。用wsl --export再wsl --import到新位置就行。
5.4 日常使用中的几个小习惯
第一,养成wsl --shutdown的习惯。WSL2 不会自动释放资源,长时间不用的时候关掉,能省不少内存。
第二,Windows 侧不要装多个版本的 NVIDIA 驱动,也不要装 GeForce Experience 的自动更新,有时候自动更新会引入兼容性问题。
第三,WSL2 里的时间可能会和 Windows 侧漂移,导致一些依赖时间戳的构建工具出问题。可以装ntpdate定期同步,或者直接wsl --shutdown重启。
第四,如果要用 Docker,建议在 WSL2 里装 Docker Engine 而不是 Docker Desktop,性能更好,资源占用更低。Docker Desktop 的 WSL2 后端虽然方便,但多了一层抽象。
这套环境我从最初的磕磕绊绊到现在基本稳定运行,前后大概花了小半年时间打磨。现在的工作流是:Windows 侧用 VS Code 写代码,通过 Remote-WSL 插件直接连到 WSL2 里,终端、调试、Git 全在 Linux 侧,GPU 训练也在 Linux 侧跑,Windows 只负责桌面和浏览器。这套组合的体验,说实话比我以前用双系统的时候好太多了。如果你也在纠结 Windows 和 Linux 之间的选择,WSL2 加 GPU 直通这条路值得认真走一遍。