把 WSL 当开发主力机:文件应该放哪,工具应该在哪跑
原创 · 约 24 分钟阅读 · 阅读 --

把 WSL 当开发主力机:文件应该放哪,工具应该在哪跑

作者: Alex Xiang


古董级程序员,大厂出来后一直在创业公司,现在仍活跃在一线做 AI 相关的开发。更完整的更新写在微信公众号「字与码」:工作经历、对新技术的想法,以及这些年走弯路的记录,会不定期发在那里。若觉得博客对你有用,欢迎顺手关注。

我见过不少 WSL 环境,最常见的问题不是装不上 Ubuntu,也不是不会配 VS Code,而是项目放错了地方。

很多人一开始会把代码放在 D:\work\project,然后在 WSL 里进入 /mnt/d/work/project 开发。这个选择很自然:Windows 里能看到,资源管理器能打开,备份软件也熟悉。但项目稍微大一点,git status 变慢,npm install 变慢,测试启动变慢,连编辑器监听文件变化都开始不稳定。

这篇文章只讲一个问题:如果把 WSL 当成日常开发主力机,文件应该放在哪,工具应该在哪跑,什么时候才跨 Windows/Linux 边界。

把项目从 Windows 分区迁到 WSL 工作区

我会用一个混合项目做例子:后端是 Python,前端是 Node,仓库里有几万小文件,日常会跑 gituvnpmpytestvite。这类项目不算极端,但正好能暴露 WSL 的边界成本。

先说结论:让最忙的工具靠近文件

WSL2 里有两种常见路径。

一种是 Windows 文件系统挂载进来的路径,例如:

/mnt/c/Users/alex/work/api
/mnt/d/work/api

另一种是 Linux 发行版自己的文件系统,例如:

/home/alex/work/api
~/work/api

微软在 WSL 文件系统文档里明确建议:如果你在 Linux 命令行里工作,把项目文件直接放在 WSL 的 Linux 文件系统里,性能会更好。反过来,如果主要用 Windows 工具处理文件,那就放 Windows 文件系统。这个建议听起来朴素,但它是很多 WSL 性能问题的分水岭。

我的实践规则更短:

代码放在最频繁读写它的那一侧。

如果你主要用 gitnpmuvpytestmakegreprg,那它们都在 Linux 里跑,项目就放 ~/work。如果你主要用 Visual Studio、Windows 原生编译器、Windows 版数据库工具,那项目可以放 Windows 分区。不要为了“资源管理器方便看到”把高频 Linux 工作流放到 /mnt/c 下面。

这不是宗教问题,是延迟问题。

一个 Web 项目里有大量小文件:node_modules.git/objects、测试缓存、构建缓存、类型定义、锁文件。每一次 stat、打开、关闭、写入,都可能跨过 Windows 和 WSL2 的文件系统边界。单次看不出来,几万次叠起来就很明显。

一个最容易复现的慢场景

如果你不确定自己的项目是不是受影响,可以在两个位置各放一份同样的仓库:

mkdir -p ~/bench
cd ~/bench
git clone https://github.com/some-org/some-project.git linux-copy

mkdir -p /mnt/d/bench
cd /mnt/d/bench
git clone https://github.com/some-org/some-project.git windows-copy

不要选太小的仓库。最好是你正在工作的项目,或者至少是一个有几千文件、带依赖安装和测试的项目。

然后分别跑:

cd ~/bench/linux-copy
time git status
time rg "TODO" .
time npm ci
time npm test -- --runInBand

再到 Windows 分区那份:

cd /mnt/d/bench/windows-copy
time git status
time rg "TODO" .
time npm ci
time npm test -- --runInBand

这里不要太纠结单次数字。杀毒软件、磁盘、CPU、省电模式、缓存状态都会影响结果。真正有意义的是同一台机器、同一个项目、同一组命令的相对差距。

我一般会额外跑一个小文件压测,专门看“创建、读取、删除很多小文件”的成本:

cat > /tmp/wsl-file-bench.sh <<'SH'
set -euo pipefail
target="${1:?target dir}"
rm -rf "$target/wsl-bench"
mkdir -p "$target/wsl-bench"

start=$(date +%s%3N)
for i in $(seq 1 8000); do
  printf 'hello %s\n' "$i" > "$target/wsl-bench/file-$i.txt"
done
find "$target/wsl-bench" -type f -print0 | xargs -0 cat >/dev/null
rm -rf "$target/wsl-bench"
end=$(date +%s%3N)

echo "$target $((end - start)) ms"
SH

bash /tmp/wsl-file-bench.sh ~/work
bash /tmp/wsl-file-bench.sh /mnt/d/work

这个脚本不代表真实业务,但能很快把边界成本打出来。很多“WSL 慢”的抱怨,本质上不是 Linux 慢,而是把 Linux 工具放在 Windows 文件系统上反复穿墙。

正确迁移项目:不要直接拖目录

如果项目还没有复杂本地状态,最干净的方式是重新 clone:

mkdir -p ~/work
cd ~/work
git clone git@github.com:your-org/your-project.git
cd your-project

然后在 WSL 里重新安装依赖:

uv sync
npm ci

这比从 Windows 目录复制一份 node_modules 过来更可靠。node_modules 里可能有平台相关的二进制包,Python 虚拟环境也有解释器路径,直接搬经常会留下隐性问题。

如果你确实有一些未提交文件,需要从 Windows 目录带过去,我会先在旧目录里确认:

cd /mnt/d/work/your-project
git status --short

能提交就提交。不能提交的,再用 rsync 只同步源码和配置,不同步依赖目录:

rsync -av \
  --exclude node_modules \
  --exclude .venv \
  --exclude .next \
  --exclude dist \
  --exclude build \
  /mnt/d/work/your-project/ \
  ~/work/your-project/

迁完以后,在新目录跑一轮基础检查:

cd ~/work/your-project
git status --short
uv run pytest
npm test

这一步不要省。迁移环境最怕“看起来能跑”,几天后才发现某个生成目录、软链接或本地配置丢了。

VS Code 应该怎么打开

我平时不从 Windows 版 VS Code 直接打开 \\wsl.localhost\Ubuntu\home\alex\work\project。可以打开,但那样容易把编辑器扩展、文件监听和终端上下文搞得半 Windows 半 Linux。

更稳定的方式是用 Remote - WSL:

cd ~/work/your-project
code .

此时 VS Code 窗口左下角会显示它连接到了 WSL。终端、扩展、语言服务都在 Linux 侧运行。你在编辑器里保存文件,实际写的是 Linux 文件系统;你在终端里跑 pytest,读的也是同一套文件。

如果要从 Windows 资源管理器看这个目录,不要把项目搬回 Windows 分区。直接在 WSL 里执行:

explorer.exe .

它会打开当前 Linux 目录对应的 Windows 资源管理器视图。偶尔拖一个文件、看一张图片、把构建产物发给别人,这样就够了。不要反过来,让 Linux 的高频工具长期跑在 /mnt/d 上。

路径互转:少记,直接问系统

WSL 里有几个工具值得记住。

Linux 路径转 Windows 路径:

wslpath -w ~/work/your-project

输出类似:

\\wsl.localhost\Ubuntu\home\alex\work\your-project

Windows 路径转 Linux 路径:

wslpath -u 'D:\work\your-project'

输出:

/mnt/d/work/your-project

从 WSL 调 Windows 程序:

explorer.exe .
notepad.exe README.md
powershell.exe -NoProfile -Command "Get-ChildItem"

从 Windows 调 WSL 命令:

wsl.exe -d Ubuntu -- bash -lc "cd ~/work/your-project && git status --short"

这些互调能力很好用,但我只把它当“桥”,不把它当“路”。桥是过河用的,不是让你在桥上盖办公室。

.wslconfig 不是性能魔法,但能避免误判

有些人一遇到 WSL 慢,就开始调内存和 CPU。这个方向不是完全没用,但要放在文件位置之后。

全局配置在 Windows 用户目录的 .wslconfig

[wsl2]
memory=16GB
processors=8
swap=8GB

改完以后:

wsl --shutdown

再重新打开 WSL。

这类配置适合解决“构建时内存不够”“并行编译抢满 CPU”“swap 过大拖慢磁盘”的问题。它不能消除 /mnt/c 上小文件操作的边界成本。也就是说,如果项目位置错了,先搬项目;搬完还慢,再看资源限制。

单个发行版的配置在 /etc/wsl.conf。例如你想控制自动挂载行为、默认用户、systemd、interop:

[boot]
systemd=true

[interop]
enabled=true
appendWindowsPath=false

[automount]
enabled=true
options=metadata,umask=22,fmask=11

我个人会谨慎使用 appendWindowsPath=false。它能减少 PATH 污染,避免误调用 Windows 版 node.exepython.exe,但也会让一些跨系统小工具不那么顺手。团队里最好写进 onboarding 文档,不要每个人各配一套。

哪些文件还应该留在 Windows

不是所有东西都应该搬进 WSL。

适合留在 Windows 的:

  • Office 文档、设计稿、截图、临时下载文件;
  • Windows 原生工具主要处理的项目;
  • 需要被 Windows 备份软件、同步盘直接托管的资料;
  • 游戏、影音、大型非开发素材。

适合放进 WSL 的:

  • Linux 工具链主导的源码;
  • node_modules.venv.pytest_cache.next 这类高频读写目录;
  • Docker bind mount 会频繁访问的服务代码;
  • 需要大量 gitrgfindmake 操作的仓库。

有一个边界很容易踩:数据库数据目录。

如果 PostgreSQL、MySQL、Redis 运行在 WSL 或 Docker 的 Linux 环境里,数据目录不要放 /mnt/c。数据库对 fsync、锁、权限、大小写、文件改名语义都比较敏感。开发环境图省事还好,真把数据目录挂到 Windows 分区,出问题时很难排查。

一个团队可复制的目录约定

个人机器可以随意一点。团队里建议统一目录,避免文档里到处都是个人路径。

我会推荐:

~/work/company/
  product-api/
  product-web/
  scripts/

~/tmp/
  scratch/
  downloaded-artifacts/

~/data/
  local-postgres/
  local-minio/

然后把项目 README 里的命令写成 Linux 路径:

cd ~/work/company/product-api
uv sync
uv run pytest

Windows 侧需要打开目录时,统一使用:

explorer.exe .

如果有人坚持从 Windows 资源管理器进入,也让他访问:

\\wsl.localhost\Ubuntu\home\<user>\work\company\product-api

而不是把项目复制回 D:\work

迁移后的验证清单

搬完项目后,我通常按这个顺序检查:

pwd
mount | head
git status --short
git config --show-origin core.autocrlf
which node
which python
which uv
which npm

重点看三件事。

第一,pwd 不能在 /mnt/c/mnt/d 下面。

第二,which nodewhich python 不应该指向 Windows 路径。如果看到 /mnt/c/Program Files/nodejs/node.exe,说明 PATH 混进来了。

第三,换行符设置要稳定。跨 Windows 和 Linux 开发时,core.autocrlf.gitattributes 最好由项目固定,不要靠个人全局配置碰运气。

一个常见 .gitattributes 可以这么写:

* text=auto eol=lf

*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

这不是 WSL 独有问题,但 WSL 会把它放大:同一个仓库既被 Windows 工具碰,又被 Linux 工具碰,换行符和权限位很容易变成无意义 diff。

我现在的使用边界

我的日常边界大概是这样:

代码、依赖、测试、构建都在 WSL。
浏览器、聊天、Office、图片编辑大多在 Windows。
需要共享文件时,从 WSL 用 explorer.exe . 打开。
需要临时调用 Windows 命令时,用 powershell.exe 或具体 .exe
需要长期运行的服务,尽量让它在一侧闭环,不要数据在 Windows、进程在 Linux、配置又在第三处。

WSL 最舒服的状态,不是把 Windows 变成 Linux,也不是把 Linux 嵌进 Windows 当玩具。它更像一张开发工作台:Linux 负责工程工具链,Windows 负责桌面和生态,两边通过少数明确的边界连接。

文件放对以后,很多问题会自然消失。git status 不再让人怀疑人生,npm ci 不再像在慢慢拷贝沙子,测试也不会因为文件监听或路径转换冒出一堆怪问题。更重要的是,团队文档可以变简单:进入 WSL,cd ~/work/project,跑命令。不要每次都先解释为什么你的 /mnt/d 比他的 /home 慢。

参考资料