玩WSL的人多半都撞过同一堵墙:C盘空间莫名其妙见底,虚拟磁盘里的文件明明没装多少,可Windows那边那个vhdx文件却越来越大。更让人头疼的是,真等根目录被塞满,很多服务直接罢工,Docker拉不动镜像,编译写到一半报磁盘满,连登录都可能卡壳。
这篇文章就把WSL2扩容这件事完整拆开讲一遍:从搞清楚磁盘为什么只涨不瘪,到用Diskpart、Resize-VHD把虚拟磁盘真正撑大,再到进入Linux把文件系统对齐扩容,最后聊聊清理瘦身、Trim回收和防止二次爆仓的日常手段。无论你是刚接触WSL的萌新,还是被虚拟磁盘容量折磨了很久的老手,照着做都能解决问题。
1. 先把WSL的磁盘空间“底账”查清楚:虚拟磁盘与文件系统的关系
1.1 先用一条命令确认:你用的是WSL2还是WSL1
很多人一上来就卡在“我的WSL是不是虚拟磁盘”这个问题上。WSL1和WSL2完全是两种架构:WSL1把Linux系统调用翻译成Windows调用,文件直接存放在Windows目录里,压根不存在独立虚拟磁盘,也就谈不上“扩容”,只要所在分区有空间,它就能继续用。
WSL2则是跑在轻量级虚拟机里的完整Linux内核,整个Linux文件系统被封装在一个VHD虚拟磁盘文件中,文件名通常是ext4.vhdx。你在这个虚拟磁盘里能够使用的空间,上限由这个VHD文件的大小决定,而不是由C盘剩余空间直接决定。
先确认自己当前的发行版是哪个版本:
bash复制wsl -l -v
如果输出中“VERSION”列是2,那后面所有扩容操作都适用;如果是1,要么把目录搬到空间更富裕的分区,要么干脆把发行版从WSL1升级到WSL2。升级命令也很简单:
bash复制wsl --set-version <发行版名称> 2
顺便说一句,只要支持,我通常建议都升到WSL2。WSL1在文件和网络性能上跟WSL2差距明显,Docker、数据库这类场景基本没法在WSL1上正常用。不要为了省那点虚拟磁盘空间放弃WSL2,后面文里会讲怎么把空间管好。
1.2 查看当前磁盘水位:不要凭感觉判断
搞清楚版本之后,进入WSL先看两样东西:Linux侧的文件系统使用率,以及Windows侧vhdx文件的实际大小。
bash复制df -hT
重点关注挂载在 / 上的那一行。WSL2默认根文件系统是ext4,容量显示一般跟vhdx的虚拟大小一致,不会自动超出。注意不要混淆 /mnt/c 这种Windows挂载目录,那是访问C盘的入口,跟WSL虚拟磁盘扩容没有直接关系。
Windows侧查看vhdx文件大小,我在PowerShell里通常这么找:
powershell复制Get-ChildItem -Path "$env:LOCALAPPDATA\Packages" -Filter "ext4.vhdx" -Recurse | Select-Object FullName, @{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
这一步能同时看到路径和实际文件体积。Linux里用du统计的总占用,和Windows里vhdx的文件大小往往对不上,这不是错觉,而是虚拟磁盘“精简置备”机制的直接体现。vhdx文件会按需增长,但删除文件后它不会自动收缩,需要人为触发Trim和压缩,这块内容放到第5节详细说。
1.3 扩容前先做个快照式备份
扩容本身虽然很成熟,但一旦涉及修改虚拟磁盘文件、文件系统大小,风险是真实存在的。建议至少在操作前看一眼有没有重要数据,最简单有效的保护手段就是导出分发版:
bash复制wsl --shutdown
wsl --export <发行版名称> D:\backup\wsl-backup.tar
这条命令把整个WSL实例打包成一个tar文件,后面万一扩容过程出了意外,可以用wsl --import再恢复。第一次导出的tar通常很大,几十GB都可能,但花费这一两分钟换的是“操作不慌”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一次扩容:用Diskpart把WSL虚拟磁盘和分区同时撑大
2.1 关闭发行版,找到那颗“虚拟硬盘文件”
所有扩容操作的第一步永远是:
bash复制wsl --shutdown
这条命令会停止所有WSL实例,释放vhdx文件的占用。如果不执行,后面操作会报文件被占用,或者Windows认为虚拟磁盘处于使用中无法修改。
关闭之后,进入刚才查到的路径,确认ext4.vhdx文件存在。路径一般长这样,发行版不同对应的包名目录不同:
text复制C:\Users\<你的用户名>\AppData\Local\Packages\<发行版包名>\LocalState\ext4.vhdx
实际操作中不需要背路径,用上面那段PowerShell命令直接输出即可。拿到完整路径后,复制出来备用。
2.2 Diskpart完整操作流程:attach、extend、detach
这是最经典、最不依赖额外组件的扩容方式,几乎适用于所有Windows版本。打开管理员命令行,输入:
text复制diskpart
然后依次执行,其中文件路径换成你自己的:
text复制select vdisk file="C:\Users\<你的用户名>\AppData\Local\Packages\<发行版包名>\LocalState\ext4.vhdx"
attach vdisk
list disk
attach之后,vhdx会作为一块新磁盘出现在系统里。此时用list disk能看到多了一块“磁盘”,它的大小就是当前虚拟磁盘的容量。有一点特别重要:不要靠盘符或编号猜哪块是WSL,务必用detail disk确认:
text复制select disk 1
detail disk
detail disk会显示这块磁盘的型号、大小、卷信息。选成物理磁盘的后果很严重,所有数据都会被误伤。如果发现选错了,重新list disk,再选一次。找到正确的磁盘之后,继续:
text复制list partition
select partition 1
extend
detach vdisk
exit
extend会把分区扩展到虚拟磁盘剩余所有未分配空间。WSL2默认磁盘里只有一个Linux分区,所以这里选partition 1是安全的。执行完之后,vhdx就被放大到我们想让它达到的容量。
这个方案还有一个隐含的逻辑需要讲清楚:“扩容”包含两级操作,一级是把VHD文件本身变大,另一级是把里面的分区变大。Diskpart里先attach让Windows识别这块虚拟磁盘,再extend让最后一个分区吞掉空白空间。如果你没做extend,只是把VHD变大了,里面分区仍然很小,等于白干。
3. 进Linux补最后一刀:不跑resize2fs等于白扩
3.1 为什么Diskpart扩完,Linux还是显示旧容量
Diskpart的extend只是把分区表里的分区大小改了,但分区里文件系统的元数据还停留在原来的大小范围。打个比方,买房时房本面积变了,但房间里的墙还没来得及拆,实际能用的区域一点没变。
Linux侧需要用文件系统自带的扩容工具,让ext4文件系统感知到分区变大了,把inode和块组扩展到新区域。这一步对WSL2这个场景是必须的,跳过去的话,重启WSL后df -h看到的依然是旧容量。
3.2 用lsblk认准设备名,再在线扩容
重新启动WSL,先看看现在的块设备布局:
bash复制lsblk
一般情况下,WSL2虚拟磁盘对应的设备是/dev/sda,分区是/dev/sda1。确认设备名之后再扩容:
bash复制sudo resize2fs /dev/sda1
ext4文件系统支持在线扩容,不需要先卸载,这在生产服务器上都有成熟实践。WSL里跑这个命令通常非常快,一块几百GB的磁盘几秒到几十秒就能完成。
执行完之后再用df -hT /确认,这时候容量应该已经变成新的目标值了。如果这里看到容量没变,大概率是设备名选错了,或者分区本身没扩过,回头用lsblk重新确认后再执行一次。
3.3 扩容期间如果报错,别慌
偶尔会遇到resize2fs提示文件系统有异常、需要先运行fsck。这种情况通常与之前非正常关机或宿主机崩溃有关。稳妥的做法是:
bash复制sudo fsck -f /dev/sda1
注意,运行fsck前必须确保分区没有被挂载,否则会报错。WSL里把根文件系统卸载是很麻烦的,所以一般建议在Windows侧关机后用专门的环境检查。好在绝大多数WSL实例不会遇到这个坑。万一真遇到,先做完整备份,再谨慎处理。
4. 两条更省事的替代路线:Resize-VHD与整体迁移换盘
4.1 PowerShell一行命令把vhdx加到指定大小
如果系统里能加载Hyper-V管理模块,有一条比Diskpart更简洁的路子:
powershell复制Import-Module Hyper-V
Resize-VHD -Path "C:\...\ext4.vhdx" -SizeBytes 200GB
和Diskpart一样,执行前先wsl --shutdown。这条命令只改虚拟磁盘容量,不处理内部分区,所以执行完还是要进Linux跑一次resize2fs。
Resize-VHD的优势是命令短、参数直观,适合不愿跟diskpart交互脚步的读者。但它有个隐性的前提:Hyper-V模块可用。Windows家庭版往往没有完整加载Hyper-V管理工具,这时用Diskpart反而更通用。两条路最终效果一致,选一条就好,没必要重复执行。
4.2 C盘本身不够大?用导出导入把整个WSL搬到别的盘
理论上VHD最大可以撑到几百GB,可如果宿主物理磁盘(比如C盘)本身就没那么多总空间,再扩大虚拟磁盘也是空中楼阁。真正的解法是搬家。
搬家靠的还是WSL自带的导出导入能力:
bash复制wsl --shutdown
wsl --export <发行版名称> D:\backup\wsl-migrate.tar
mkdir D:\WSL\ubuntu-new
wsl --import <新发行版名称> D:\WSL\ubuntu-new D:\backup\wsl-migrate.tar --version 2
这里--import的第二个参数是新的vhdx存放目录,把它放在空间充裕的D盘或E盘,等于把之前C盘虚拟磁盘消耗的空间彻底挪走。同时因为tar文件是按内容封装的,虚拟磁盘中未使用的空洞在传输过程中不会保留,所以导入出来的vhdx天然更紧凑。
迁移完成后,原来的旧实例可以保留几天作为备份,确认新环境一切正常后再删除:
bash复制wsl --unregister <旧发行版名称>
有一个迁移后必然要处理的细节:wsl --import导入的实例默认用户是root,直接wsl <新发行版名称>会变成root登录,文件权限和SSH配置都可能带来小麻烦。解决方式是在WSL内新建或修改/etc/wsl.conf:
ini复制[user]
default=你的用户名
然后执行wsl --shutdown再启动,默认用户就恢复正常了。
5. 扩容只是治标:清理、Trim回收、防二次爆仓的三件套
5.1 先找出真正吃空间的大户,别盲目删
很多人一看到磁盘满了就急着扩容,其实很多空间是垃圾、缓存、旧镜像堆出来的。先看一下根目录下哪些地方占得多:
bash复制sudo du -h --max-depth=1 / 2>/dev/null | sort -h
常见的大户集中在:
/var/lib/docker:Docker镜像、容器层、构建缓存/home/<用户名>/:隐藏目录如.cache、.local、.conda/usr/local/:conda环境、自编译软件swap文件:默认可能占了几GB
清理动作按优先级来:
bash复制sudo apt clean
docker system prune -a -f
pip cache purge
conda clean -a -y
这几条清出来的空间往往比想象中多。任何扩容方案都不应该成为代替清理的偷懒方式,先瘦身再扩容,效果才是最大化。
5.2 vhdx只涨不缩的真相:Trim信息没通知宿主
虚拟磁盘之所以能保持较小体积,依赖的是“稀疏文件”机制:文件系统删除数据后,会向存储层发送TRIM指令,告诉底层这些块可以回收。WSL2需要主动命令它Trim:
bash复制sudo fstrim -v /
跑完,回到Windows侧,对vhdx执行压缩,常见两种命令:
powershell复制Import-Module Hyper-V
Optimize-VHD -Path "C:\...\ext4.vhdx" -Mode Full
或者:
powershell复制Compact-VHD -Path "C:\...\ext4.vhdx"
操作前同样要wsl --shutdown。压缩前后对比非常直观,我见过一台装过大型镜像的机器,vhdx从80GB压缩到20GB出头。在repeat的过程中我有过一个小体会:不要等爆仓才做这套动作,定期跑一次fstrim加压缩,能让Windows宿主空间一直处于透气状态。
5.3 顺手管住swap和日志,下次扩容间隔拉长
WSL2会根据需求自动创建swap文件,默认大小与内存有关,有时候会占用不小的虚拟磁盘。先看看当前swap情况:
bash复制cat /proc/swaps
如果swap文件确实过大,可以调整配置。通常做法是关闭现有swap、修改/etc/fstab里的swap路径和大小、重新创建swap文件。整个动作不复杂但需要小心翼翼,可以先备份后再操作。
日志和临时文件同样值得注意。journald默认可能攒下数百MB甚至更多日志:
bash复制sudo journalctl --vacuum-size=200M
日常养成定期清理的习惯,下一次“扩容”可能要到很久以后才需要。
6. 实战里最容易翻车的五个细节
这里直接上表格,遇到问题可以先对照找根因:
| 现象 | 根因 | 处理方式 |
|---|---|---|
df -h 显示容量没变化 |
扩了虚拟磁盘/分区,但没执行resize2fs |
进入WSL执行sudo resize2fs /dev/sda1 |
| Diskpart里找不到WSL的磁盘,或不确定是哪块 | attach之后没刷新磁盘列表,或误选物理磁盘 | 重新list disk,用detail disk核对大小和型号 |
| 报错“虚拟磁盘文件正被使用” | WSL没完全关闭,vhdx被占用 | 回到Windows下执行wsl --shutdown,再重试 |
| 迁移后默认用户是root,SSH密钥路径变了 | wsl --import重建了分发版默认配置 |
修改/etc/wsl.conf设置默认用户,重启WSL |
| vhdx文件即使删了东西也不变小 | 文件系统删除后未Trim,宿主无法缩水 | WSL内跑fstrim,Windows侧执行Optimize-VHD或Compact-VHD |
每个坑展开说两句。
第一,忘跑resize2fs是最高频的错误,没有之一。很多朋友执行完Diskpart或Resize-VHD就急着开WSL看容量,一看没变化就开始折腾重装系统。别慌,进Linux跑一遍文件系统扩容,基本就解决了。
第二,Diskpart选盘问题是最危险的一个细节。虚拟磁盘attach之后,Windows会把它识别成新磁盘,列表里的盘号可能不带任何容易辨认的特征,贸然执行select disk 1再detail disk,如果显示的是物理硬盘,立刻回头重新选。
第三,文件占用问题。之前遇到过反复优化VHD失败,排查半天才发现有个WSL进程还在后台跑着,wsl --shutdown没有真正生效,等几秒再确认进程消失。
第四,迁移后默认用户变化,几乎每个走wsl --import的人都踩过。改wsl.conf是最干净的做法,不要试图手动改注册表里的默认用户键值,那个方式容易出幺蛾子。
第五,Trim和压缩是很多人的知识盲区。扩容只解决“容量上限”问题,而vhdx的“实际占用”需要Trim+压缩这套组合拳。尤其注意顺序:先WSL内fstrim,再Windows侧Optimize-VHD,顺序反了效果差很多。
说到最后再分享一个实用习惯:我一般会在每月固定时间做一次WSL“轻保养”,花十分钟执行三件事——进WSL跑fstrim -v /,清一遍docker、pip、apt缓存,再回到Windows对vhdx执行一次压缩。这套动作做下来,虚拟磁盘基本不会无缘无故膨胀,C盘空间长期保持健康。如果哪位读者正准备从Diskpart之路开始第一次扩容,记得每一步操作前都先备份,哪怕只是导出一份tar,也能让你在遇到意外时全身而退。
