WSL2 虚拟磁盘 ext4.vhdx 占用过大缩减教程


WSL2 虚拟磁盘 ext4.vhdx 占用过大缩减教程

本文适用于这种情况:Windows 里看到某个 WSL2 的 ext4.vhdx 文件很大,比如 90GB、100GB,但进入 WSL 后用 df -h 查看,Linux 内部实际只用了十几 GB。出现这种情况并不是 WSL 坏了,而是因为 WSL2 使用的是动态虚拟硬盘。它会随着使用自动变大,但你在 Linux 里删除文件后,外层的 ext4.vhdx 通常不会自动缩小,所以需要手动压缩。

以 Ubuntu-22.04 为例,假设它的虚拟磁盘路径类似下面这样:

C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ext4.vhdx

这个路径是正常的。不同 WSL 发行版可能在不同目录下,例如有的在:

C:\Users\xxx\AppData\Local\Packages\...\LocalState\ext4.vhdx

也有的在:

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

路径不同只是安装方式不同,不代表异常。真正要判断的是:Windows 看到的 ext4.vhdx 大小,和 WSL 内部实际使用量是否差距很大。


查询 WSL 名称和状态

先打开 PowerShell,执行:

wsl -l -v

你会看到类似:

  NAME            STATE           VERSION
* Ubuntu-22.04    Running         2
  Ubuntu-20.04    Stopped         2
  Ubuntu-24.04    Stopped         2

这里要确认你准备缩减的是哪个发行版。后面的教程以:

Ubuntu-22.04

为例。如果你的名字不同,比如 UbuntuUbuntu-24.04,后面命令里的名字要对应修改。


查询 WSL 的真实磁盘路径

不要自己去文件夹里乱找,也不要手动复制很长的 GUID 路径。最稳的方法是从注册表里读取 WSL 的真实路径。

在 PowerShell 中执行:

Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object {
    $p = Get-ItemProperty $_.PSPath
    [PSCustomObject]@{
        Name = $p.DistributionName
        BasePath = $p.BasePath
        Version = $p.Version
    }
}

你会看到类似:

Name           BasePath
----           --------
Ubuntu-22.04   C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
Ubuntu-20.04   C:\Users\xxx\AppData\Local\Packages\...\LocalState
Ubuntu-24.04   C:\Users\xxx\AppData\Local\Packages\...\LocalState

其中 BasePath 就是对应 WSL 虚拟磁盘所在的目录。


自动获取 Ubuntu-22.04 的 ext4.vhdx 路径

为了避免路径输错,建议直接用变量保存路径。执行:

$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }

$vhd = Join-Path $p.BasePath "ext4.vhdx"

$vhd
Test-Path -LiteralPath $vhd

正常情况下会输出类似:

C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ext4.vhdx
True

只要最后是:

True

就说明路径正确。

如果是:

False

说明没有找到 ext4.vhdx,这时不要继续压缩,先检查发行版名字是不是写错了。


查看压缩前 ext4.vhdx 的大小

继续在 PowerShell 中执行:

Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}

你会看到类似:

FullName                                                               SizeGB
--------                                                               ------
C:\Users\xxx\AppData\Local\wsl\{...}\ext4.vhdx                         95.65

这里的 SizeGB 就是 Windows 当前实际被这个虚拟磁盘文件占用的空间。


查看 WSL 内部实际使用量

接着查看 Ubuntu-22.04 内部到底用了多少空间:

wsl -d Ubuntu-22.04 -- df -h /

如果结果类似:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sdc        1007G 17G  939G  2%   /

说明 Linux 内部实际只用了 17GB。此时如果 Windows 上的 ext4.vhdx 是 95GB,就说明中间有大量已经释放但没有被 Windows 回收的空间,可以进行压缩。

如果这里 Used 也很大,比如 80GB、90GB,那就不是虚拟磁盘没缩的问题,而是 WSL 里面确实有大文件,需要先清理 Linux 内部文件,再压缩。


执行 fstrim 标记可回收空间

在压缩之前,先让 WSL 标记哪些空间已经不用了:

wsl -d Ubuntu-22.04 -- bash -lc "sudo fstrim -av"

这个命令可能会要求输入 Ubuntu 里的 sudo 密码。执行后可能会看到类似:

/: 70 GiB trimmed

这表示 Linux 内部有一部分空闲块已经被标记为可以回收。即使输出比较少,也可以继续下一步。


关闭 WSL

压缩虚拟磁盘前,必须关闭 WSL。执行:

wsl --shutdown

然后检查状态:

wsl -l -v

最好确认 Ubuntu-22.04 已经是:

Stopped

如果还显示 Running,等几秒后再执行一次:

wsl --shutdown

使用 DiskPart 压缩 ext4.vhdx

如果你的系统支持 Optimize-VHD,可以使用它。但很多 Windows 家用版或未启用 Hyper-V 模块的系统没有这个命令,所以这里直接使用更通用的 DiskPart 方法。

建议用管理员 PowerShell执行下面这一整段:

$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }

$vhd = Join-Path $p.BasePath "ext4.vhdx"

if (-not (Test-Path -LiteralPath $vhd)) {
    Write-Host "找不到 VHDX 文件:$vhd"
    exit
}

wsl --shutdown

@"
select vdisk file="$vhd"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | Set-Content "$env:TEMP\compact-wsl.txt" -Encoding ASCII

diskpart /s "$env:TEMP\compact-wsl.txt"

这段命令会自动找到 Ubuntu-22.04 的 ext4.vhdx,生成 DiskPart 脚本,然后执行压缩。这样做的好处是不用你手动输入长路径,也不会因为 GUID 少输一个字符而失败。

成功时,DiskPart 一般会显示类似:

DiskPart 成功地选择了虚拟磁盘文件。
DiskPart 成功地附加了虚拟磁盘文件。
DiskPart 成功地压缩了虚拟磁盘文件。
DiskPart 成功地分离了虚拟磁盘文件。

其中最关键的是:

DiskPart 成功地压缩了虚拟磁盘文件。

验证压缩结果

压缩完成后,再查看一次 ext4.vhdx 的大小:

Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}

如果 PowerShell 变量 $vhd 已经失效,就重新执行:

$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }

$vhd = Join-Path $p.BasePath "ext4.vhdx"

Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}

正常情况下,原来 95GB 左右的 ext4.vhdx 可能会降到 20GB 到 30GB 左右。不一定会刚好等于 df -h 里的 17GB,因为虚拟磁盘还会保留文件系统元数据和一些结构空间。

只要它明显变小,就说明缩减成功。


启动 WSL 检查是否正常

最后重新启动 Ubuntu-22.04:

wsl -d Ubuntu-22.04

进入后执行:

df -h /

确认系统可以正常进入,空间显示正常即可。

退出 WSL:

exit

最终推荐命令合集

如果你已经确认要缩减的是 Ubuntu-22.04,可以直接用下面这段。用管理员 PowerShell运行:

$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }

$vhd = Join-Path $p.BasePath "ext4.vhdx"

Write-Host "目标 VHDX:$vhd"

if (-not (Test-Path -LiteralPath $vhd)) {
    Write-Host "错误:找不到 VHDX 文件,停止操作。"
    exit
}

Write-Host "`n压缩前大小:"
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}

Write-Host "`n执行 fstrim..."
wsl -d Ubuntu-22.04 -- bash -lc "sudo fstrim -av"

Write-Host "`n关闭 WSL..."
wsl --shutdown

@"
select vdisk file="$vhd"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | Set-Content "$env:TEMP\compact-wsl.txt" -Encoding ASCII

Write-Host "`n开始压缩 VHDX..."
diskpart /s "$env:TEMP\compact-wsl.txt"

Write-Host "`n压缩后大小:"
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}

这版流程就是:先自动找到 Ubuntu-22.04 的 ext4.vhdx,显示压缩前大小,执行 fstrim,关闭 WSL,使用 DiskPart 压缩,最后显示压缩后大小。路径里的用户名和 GUID 都不需要手动输入,避免出错。


文章作者: 0xdadream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0xdadream !
评论
  目录