WSL2 的 VHDX 为什么只增不减

太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处
WSL2WindowsVHDXDiskPart

资料

这算是 WSL2 很容易踩到的坑。发行版里的文件都放在 ext4.vhdx 中,这个动态虚拟磁盘会随着数据写入不断增大。问题出在删除文件以后:Linux 看到的可用空间回来了,Windows 上的 VHDX 文件却不会跟着变小。

我遇到过的情况很直接。构建缓存、容器镜像和临时文件清理完了,df 看起来很正常,C 盘空间却没有回来。微软的 compact vdisk 文档也明确写着,动态 VHD 会随数据增加而变大,但删除数据后不会自动缩小。WSL 仓库里的 #4699 从 2019 年就在讨论同一个问题。

默认安装目录也不好找

旧的商店 Appx 发行版通常把虚拟磁盘放在类似下面的位置:

C:\Users\USER\AppData\Local\Packages\CanonicalGroupLimited...\LocalState\ext4.vhdx

新版 WSL 使用新的发行版架构后,也可能看到这种路径:

C:\Users\USER\AppData\Local\wsl\{GUID}\ext4.vhdx

无论是带发布者后缀的包目录,还是只有一串 GUID 的目录,都不适合靠记忆寻找。PowerShell 可以从 WSL 的注册信息中取出准确路径:

$distroName = 'Ubuntu'
(Get-ChildItem -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss' |
  Where-Object { $_.GetValue('DistributionName') -eq $distroName }
).GetValue('BasePath') + '\ext4.vhdx'

不要在 AppData 里直接剪切正在使用的 VHDX。微软也提醒过,使用 Windows 工具随意修改这些文件可能损坏发行版。

我的习惯是放在 C:\app\wsl

我会把自己管理的软件放在 C 盘的 C:\app,WSL 也一样。这不是为了把文件赶到另一个盘符,而是为了让安装位置一眼就能找到:

C:\app\wsl\Ubuntu\ext4.vhdx

新版 WSL 安装发行版时可以直接指定位置:

wsl --install --distribution Ubuntu --location C:\app\wsl\Ubuntu

我当前使用的 WSL 也支持移动已有发行版:

wsl --shutdown
wsl --manage Ubuntu --move C:\app\wsl\Ubuntu

先运行 wsl --help,确认本机有 --location--move。版本太旧时不要手动搬 VHDX,可以沿用导出、注销、再导入的办法:

wsl --export Ubuntu C:\app\wsl\Ubuntu.tar
Get-Item -LiteralPath 'C:\app\wsl\Ubuntu.tar'

# 确认导出文件存在且大小正常后再注销
wsl --unregister Ubuntu
wsl --import Ubuntu C:\app\wsl\Ubuntu C:\app\wsl\Ubuntu.tar --version 2

wsl --unregister 会删除原发行版。这里不能省掉导出文件检查,也不要在备份尚未完成时提前注销。

用 DiskPart 收回空间

先在 WSL 中删除确实不再需要的文件。然后关闭所有 WSL 实例;如果使用 Docker Desktop,也先退出它。

以管理员身份打开 PowerShell:

wsl --shutdown
diskpart

进入 DiskPart 后执行:

select vdisk file=C:\app\wsl\Ubuntu\ext4.vhdx
attach vdisk readonly
compact vdisk
detach vdisk
exit

compact vdisk 只适用于动态扩展 VHD。磁盘需要处于分离状态,或者像这里一样只读挂载。路径也必须指向目标发行版,别在有多个 ext4.vhdx 时凭文件名猜。

如果提示文件正在使用,先检查 WSL 和 Docker Desktop 是否真的退出,再执行一次 wsl --shutdown。压缩完成后,Windows 上的 VHDX 文件会缩小;具体能收回多少,取决于发行版里有多少已经释放的磁盘块。

新版 WSL 还提供稀疏 VHD 选项,但不同版本上的回收效果曾经引起过不少讨论。我没有把它当成唯一保障。安装时指定 C:\app\wsl,需要时手动执行一次 compact vdisk,路径和结果都更容易确认。