← 返回文章列表

WSL2 的运行机制与开发环境实践

从 WSL1 的系统调用转换到 WSL2 的轻量虚拟机,梳理文件系统、网络与资源配置的工程边界。

显示开发终端与代码的笔记本电脑

在 Windows 上写后端时,经常会遇到一种不太舒服的错位:代码在 Windows 上编辑,部署环境却是 Linux。PowerShell 可以完成日常脚本工作,但它不能让 Linux 二进制、包管理器和系统调用凭空出现在 Windows 内核里。WSL2 解决的正是这段差异。

本文不把 WSL2 当成一个“Linux 终端皮肤”。它背后运行的是微软维护的 Linux 内核,发行版根文件系统放在虚拟磁盘中,Windows 再把进程、文件和网络入口接到用户熟悉的位置。

WSL1 和 WSL2 不是同一种实现

WSL1 没有运行 Linux 内核。Linux 程序发出的系统调用会经过兼容层,转换成 Windows NT 内核能够处理的操作。这种方式启动快、跨 Windows 文件系统访问直接,但兼容层必须逐个实现 Linux 行为。Docker 依赖的 namespace、cgroup 等内核能力很难靠系统调用翻译完整模拟。

WSL2 换了一个方向:不再翻译 Linux 系统调用,而是在 Hyper-V 管理的轻量虚拟机中运行真实 Linux 内核。Ubuntu 里的 forkepoll、namespace 和 cgroup 都由 Linux 内核处理。Windows 负责虚拟机生命周期、资源分配以及两边的互操作。

可以把两种路径简化成下面这样:

WSL1: Linux 程序 -> 系统调用翻译层 -> Windows NT 内核
WSL2: Linux 程序 -> Linux 内核 -> Hyper-V 轻量虚拟机 -> Windows

这也是 WSL2 能直接运行大多数 Linux 工具和容器工作负载的原因。代价是它毕竟多了一层虚拟化,跨 Windows/Linux 文件系统和网络时需要经过额外通道。

一次实机检查先卡在了虚拟磁盘

我通过 SSH 检查了一台 Windows 主机。系统 build 为 26200.9168,WSL 版本为 2.5.10.0wsl --list --verbose 显示 Ubuntu 已注册为 WSL2。真正启动 Ubuntu 时却没有进入 Shell,而是返回:

Wsl/Service/CreateInstance/MountDisk/HCS/ERROR_FILE_NOT_FOUND

错误中同时给出了发行版原来的 ext4.vhdx 路径。这个文件已经不存在,所以 WSL 能列出发行版,却无法挂载根文件系统。它说明“发行版注册信息”和“发行版磁盘”是两层状态:前者还在,不代表后者可用。

本文后面的 I/O 和 localhost 命令因此只作为可复现步骤保留,没有填写虚构的耗时结果。修复这类故障通常涉及找回 VHDX、注销后重新导入或重装发行版,都会改变现有数据,不应该在只读检查阶段直接执行。

发行版实际放在哪里

WSL2 发行版通常使用动态扩容的 ext4.vhdx 保存 Linux 根文件系统。进入 Ubuntu 后看到的 /home/var/usr 位于这个 ext4 文件系统中,而 /mnt/c 是通过 DrvFs 挂载的 Windows 磁盘。

这两个位置能互相访问,不代表性能一样。Node.js 依赖安装、Git checkout、CMake 编译会创建和扫描大量小文件。如果工具运行在 Linux 侧,项目也应放在 Linux 文件系统,例如:

mkdir -p ~/projects
cd ~/projects
git clone https://gitcode.com/gh_mirrors/de/deepseek-harness.git

不要因为 Windows 资源管理器看起来方便,就默认把项目放到 /mnt/c/Users/...。Windows 可以通过 \\wsl.localhost\Ubuntu\home\<user>\projects 打开 Linux 文件,VS Code Remote WSL 也会让界面留在 Windows、语言服务和终端运行在 WSL 中。

一个很小的文件系统实验

下面的脚本分别在 Linux 根文件系统和 Windows 挂载盘创建 3000 个小文件。它不追求严谨的基准测试,只用来暴露跨文件系统的固定成本。

set -eu

bench() {
  target="$1"
  rm -rf "$target"
  mkdir -p "$target"
  time sh -c 'i=0; while [ "$i" -lt 3000 ]; do printf "%s\n" "$i" > "$1/$i.txt"; i=$((i+1)); done' sh "$target"
  rm -rf "$target"
}

bench "$HOME/wsl2-io-demo"
bench "/mnt/c/Temp/wsl2-io-demo"

正式测试时应关闭实时扫描带来的干扰,多跑几轮并记录中位数。这里真正要验证的是目录位置会不会影响开发工具,而不是得出一组可以套用到所有机器的倍数。

网络为什么有时像本机,有时又不像

WSL2 默认网络长期使用 NAT:Linux 虚拟机有自己的虚拟网卡和 IP,Windows 通过转发访问它。WSL 默认开启 localhostForwarding,所以在 WSL 中启动服务后,Windows 通常可以直接访问 localhost

先在 WSL 中启动一个服务:

mkdir -p /tmp/wsl-http-demo
printf 'hello from wsl2\n' > /tmp/wsl-http-demo/index.html
cd /tmp/wsl-http-demo
python3 -m http.server 8080 --bind 0.0.0.0

然后在 Windows 终端访问:

curl.exe http://localhost:8080

如果要让局域网其他机器访问,还要考虑 Windows 防火墙、监听地址和 NAT 转发。Windows 11 的 mirrored networking 会把两边的网络关系做得更接近同一台主机,但企业 VPN、防火墙策略和旧版 WSL 仍可能造成差异。遇到问题时先看 wsl --versionwsl --statushostname -I,不要直接把所有故障归到端口转发。

WSL2 不是越大越好

WSL2 会动态使用内存和 CPU,也可以通过 %UserProfile%\.wslconfig 设置上限:

[wsl2]
memory=6GB
processors=4
swap=2GB
networkingMode=mirrored

修改后需要执行 wsl --shutdown。资源限制要按机器调整,给 8GB 内存的电脑照抄 6GB 上限并不合理。

什么时候值得用

后端、容器、Python/Node.js/Linux 工具链是 WSL2 最合适的场景。它让开发环境更接近部署环境,同时保留 Windows IDE、浏览器和办公软件。

如果项目必须由 Windows 和 Linux 工具频繁修改同一批 Windows 文件,WSL1 的跨文件系统路径有时反而更合适。驱动开发、完整内核调试、严格网络拓扑测试也不应拿 WSL2 代替真正虚拟机或物理 Linux 主机。

WSL2 的价值不是“Windows 不如 Linux”,而是把两套工具链放在一台机器上,并把切换成本压到足够低。理解它是一台被系统管理的轻量 Linux 虚拟机,文件、网络和资源问题就容易解释了。

参考资料