niniu

一个分享和记录的博客

找回WSL2 vhdx 失落的储存空间

WSL是微软在Windows 10 1909以上版本中推出的基于Hyper-V得轻量级Linux子系统,与先前的WSL1不同由于有了虚拟化技术的支持,使得WSL2对于很多Linux特性的支持也更加完善,但是其文件系统也有原来的基于NTFS的虚拟文件系统(ext4)改成了放在虚拟磁盘上的ext4文件系统,提升了性能和可维护性,但是也带来了一些问题。

从写满磁盘的崩溃开始

最近,手头上的一个小程序需要跑在本地的电脑上,这个程序有一个特性就是在运行过程中会产生大量的临时文件(数十万计)。以前在服务器上跑的时候因为空间不成问题,都是跑完程序后清理下临时文件,临时目录也一直用的是默认的/tmp,一直用的很愉快。但是在本地的机器上跑的时候,感谢固态硬盘良好的io性能,不到一会就写满了整个磁盘,等我注意到的时候WSL2已经崩溃退出,再也进不去了。

WSL2,你醒醒啊

在WSL2已经崩溃之后,首当其冲的就是要让他活过来,考虑到是临时文件撑着了,我们首先将产生的临时文件删除,由于系统已经进不去了所以我们需要使用虚拟机中的Linux系统(或者liveCD),并将vhdx文件挂载后删除产生问题的临时文件即可。

vhdx文件一般位于:

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

附加到虚拟机后:

依次

lsblk
sudo mount /dev/your_dev /mnt/your_mountpoint
rm -rf /tmp/*.tmp

即可。

也可以另外安装一个WSL2来完成类似操作:

#列出所有可用发行版
wsl --list --online
#安装另一个发行版,此处是Ubuntu18.04
wsl --install --distribution Ubuntu-18.04

然后附加并管理原系统vhdx

#首先关闭所有正在运行的WSL
wsl --shutdown
#双击原系统的vhdx文件将其挂载,注意如果提示需要格式化,请选择否
#获取虚拟硬盘的路径
GET-CimInstance -query "SELECT * from Win32_DiskDrive"
#将磁盘挂载到指定发行版,注意此处是Ubuntu18.04
wsl --distribution Ubuntu-18.04 --mount <DiskPath>
#默认的挂载路径是/mnt/wsl/<DiskPath_suffix>
#然后删除多余的临时文件
rm -rf /mnt/wsl/<DiskPath_suffix>/tmp/*.tmp
#退出
exit
#解除挂载
wsl --shutdown
wsl --unmount <DiskPath>
#打开原有的WSL
wsl

失落的空间,找回你不容易

PS> diskpart
diskpart> select vdisk file="C:\Users\<Username>\AppData\Local\Packages\<Linux-Distribution-AppPackageName>\LocalState\ext4.vhdx"
diskpart> compact vdisk
#或者用Hyper-v提供的管理工具
PS> Optimize-VHD -Mode Full -Verbose "C:\Users\<Username>\AppData\Local\Packages\<Linux-Distribution-AppPackageName>\LocalState\ext4.vhdx"

当你做完了上面的工作之后,你会发现,vhdx有变化,但是可能只是一点点,于是参考网络上提供的资料,我们首先尝试将虚拟磁盘未使用的空间填0:

#run in wsl

sudo zerofree /dev/sda
exit
PS> wsl --shutdown

然后再次运行上面的命令:

PS> diskpart
diskpart> select vdisk file="C:\Users\<Username>\AppData\Local\Packages\<Linux-Distribution-AppPackageName>\LocalState\ext4.vhdx"
diskpart> compact vdisk
#或者用Hyper-v提供的管理工具
PS> Optimize-VHD -Mode Full -Verbose "C:\Users\<Username>\AppData\Local\Packages\<Linux-Distribution-AppPackageName>\LocalState\ext4.vhdx"

你会发现,它的体积又小了,但还是一点点,看来要达到满意的效果,我们需要考虑其他的方法。

利用导出/导入功能重新安装子系统,释放空间

首先利用下面的命令备份原系统:

wsl --export Ubuntu <backupPath>.tar

确认导出是否成功,然后卸载原子系统

利用导入命令重新安装

wsl --import Ubuntu <InstallLocation> <backupPath>.tar

完成后可以发现虚拟磁盘占用的空间已经比较正常。

检查新安装的子系统是否是默认系统:

wsl --list

如果不是,可以使用下面的命令将其设为默认:

wsl --set-default Ubuntu

重新导入的系统,在登录用户名方面可能会有问题(即在启动时默认以root用户登录),具体解决方法可见:http://www.xfy-learning.com/2020/05/30/WSL%E5%A4%87%E4%BB%BD%E4%B8%8E%E8%BF%98%E5%8E%9F/

2.2 修改wsl子系统默认用户

参考:问题#3974merkuriy的回答

其思路即修改注册表中相关的键值,即将注册表
计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\{…}
中的DefaultUid值设置为子系统用户对应的gid值(通过id -u来获取,或者直接查看/etc/passwd文件)。通过定义PowerShell函数,然后调用来实现,具体代码如下:

定义函数WSL-SetDefaultUser,接收参数distro和user
Function WSL-SetDefaultUser ($distro, $user) { Get-ItemProperty Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\*\ DistributionName | Where-Object -Property DistributionName -eq $distro | Set-ItemProperty -Name DefaultUid -Value ((wsl -d $distro -u $user -e id -u) | Out-String); };

调用函数,此处对应发行版名称Ubuntu,用户名为yz: WSL-SetDefaultUser <DistroName> <UserName>
WSL-SetDefaultUser Ubuntu yz

取消函数定义
Remove-Item Function:WSL-SetDefaultUser

参考资料:

[1]. https://docs.microsoft.com/zh-cn/windows/wsl/basic-commands

[2]. https://segmentfault.com/a/1190000040999582?utm_source=sf-similar-article

[3]. https://docs.microsoft.com/zh-cn/windows/wsl/wsl2-mount-disk

点赞

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注