先说说我这台机器的来龙去脉。我用的是 Windows 10 下的 WSL2,发行版选的 Ubuntu 18.04,本来是图省事,想在 Windows 里写写 ROS 代码、编点 C++ 小程序,图一个启动快、不折腾双系统。结果用着用着,某天终端里突然飘红:No space left on device。一开始我以为是 /tmp 满了,清理一下就算了。后来又过了一段时间,连 apt update 都跑不完,系统提示磁盘空间不足,我才意识到,问题出在 WSL2 的虚拟磁盘本身——默认 20GB 的上限,被我塞满了。
如果你想在 Windows 下长期用 WSL2 跑 Ubuntu 18.04,尤其是做机器人、SLAM、深度学习这类依赖重型库的开发,那这篇内容就是写给你看的。我会把 WSL2 磁盘扩容的原理、我踩过的坑、以及一套能从 20GB 直接扩到 200GB 的完整操作流程,全部摊开讲清楚。这不是什么高深莫测的黑科技,但照着网上零散的教程做,十有八九会在某一环节卡住,甚至搞坏整个子系统。我会尽量把“为什么这么做”也讲明白,知其然也知其所以然,遇到异常时你才有能力自己判断。
1. 先搞懂 WSL2 的虚拟磁盘是怎么“长大”的
1.1 ext4.vhdx 到底是什么
WSL2 和早期的 WSL1 完全不同。WSL1 是一个系统调用翻译层,文件操作直接映射到 Windows 的 NTFS 文件系统上;WSL2 则是一个真正运行在轻量级虚拟机里的 Linux 内核。既然是虚拟机,那 Linux 的根文件系统就不能直接铺在 Windows 的 C 盘上,而是以一块虚拟磁盘的形式存在。这块虚拟磁盘在 Windows 里的表现形式,就是一个文件,路径类似:
code复制C:\Users\<你的用户名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
注意这里的 ext4.vhdx,它不是像普通单文件那样密密麻麻占满空间。它采用的是 VHDX(Virtual Hard Disk v2)动态扩展格式。什么意思呢?就是这块虚拟磁盘有一个“上限值”,比如默认的 20GB,但实际它占用的物理空间只等于你真正写入的数据量。如果你只用了 5GB,那 ext4.vhdx 文件在 C 盘上大约也就 5GB+ 左右;当你把数据一路写到 20GB,这个文件就会慢慢膨胀到接近 20GB,并且一旦达到设定上限,就再也不能写入新数据了。
这就造成了一个很尴尬的现状:你在 WSL2 的 Ubuntu 18.04 里执行 df -h,看到根分区是 /dev/sdb 或 /dev/sda,容量固定 20GB,不会因为你把 WSL2 放在 D 盘就变成 100GB。WSL2 发行版的“系统盘”容量是在创建时由发行版安装包决定的(一般是 20GB 或 1TB,取决于 Windows 和 WSL 的版本;多数情况下是 20GB 初始上限)。
1.2 为什么 Ubuntu 18.04 特别容易爆盘
如果你只是拿 Ubuntu 18.04 来敲几个 Linux 命令、跑点简单脚本,20GB 绰绰有余。但一旦进入稍微重型一点的场景,空间就不够看了。
- ROS(机器人操作系统):如果你装的是
ros-melodic-desktop-full,那基本上一套下来就是 4~6GB,加上编译过程中产生的.o、.a中间文件、依赖的库文件,直接翻倍。 - 编译工具链:
build-essential、cmake、gcc、g++,这些虽然单个不大,但关联依赖一堆。 - CUDA 和 cuDNN:哪怕只是装个 CUDA 11.x 的 runtime 和 toolkit,也是 3~4GB 起步。
- Python 虚拟环境和 pip 缓存:
~/.cache/pip很容易积攒几个 GB 的 wheel 包。 - Docker 镜像和数据卷:很多人在 WSL2 里启用 Docker Desktop 的 WSL2 backend,镜像默认也存在这个发行版的虚拟磁盘里,几个镜像下来 10GB 就没了。
还有一个容易被忽略的:ROS 编译工作空间。我自己建了一个 catkin_ws,里面 src 代码可能不到 1GB,但 build 和 devel 目录如果你不清理,动辄 10GB 以上。尤其是 devel 目录里每一项编译产物都可能膨胀好几倍。Ubuntu 18.04 配套的是比较老的库,遇到最新代码时往往需要从源码编译,那些源码包、opencv 的 contrib 模块、PCL、Eigen,随便一个从源码编译,中间文件占个 5GB 都不奇怪。
当你发现 df -h 显示 /dev/sdb1 已经 100% 时,在 Linux 里面基本上是无解的——很多操作(比如 apt install、apt upgrade、创建临时文件)都需要写盘,但你连写一个 1KB 的文件都做不到。这时候你是没法直接在 Ubuntu 内部“删一删就完事”的,因为关键的系统缓存和 /var 目录可能早就塞满了。
1.3 磁盘满后的一系列连锁反应
在我实际经历中,WSL2 磁盘满的时候,并非所有的空间都被真实数据占用。这里面有一个 WSL2 非常典型的“坑中之坑”——Linux 删除文件后,空间并不一定立即释放回 Windows。
原因在于 VHDX 文件的释放机制。你在 Ubuntu 里 rm 掉一个大文件,ext4 文件系统层的“块”被标记为可用,但是对于承载 VHDX 的 Windows 文件系统来说,VHDX 文件自己并不清楚这些块是否已经空出来。除非你通过特殊的手段“压缩”VHDX,否则它在 Windows 上显示的物理大小,只会涨,不会自动减。
所以有时候你会遇到这样的情况:在 Ubuntu 里杀掉了很多缓存文件,df -h 也显示可用空间多了,但是 C 盘或者 D 盘的剩余空间并没有回来。这就是为什么很多人扩容 WSL2 之后,发现 C 盘还是红的——你只清理了内部虚拟磁盘,没有处理 VHDX 文件本身的物理大小。
在动手扩容之前,最好先做个心理建设:如果你的 C 盘(或 WSL2 发行版所在盘)剩余空间已经低于 10GB,那你需要先想办法腾点空间出来,因为扩容操作本身,可能需要在承载 VHDX 的那个磁盘分区上临时写入几个 GB 的数据(比如我后面要讲的导出、导入,或者用 diskpart 操作时产生的文件变化)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 两种绕不开的扩容思路:在线扩容和离线扩容
2.1 “在线扩容”的局限性,以及它是不是伪需求
很多教程会教你一种听起来很“优雅”的方法:用 wsl --resize 命令,或者在 WSL 配置文件中设置 [automount] 和 [wsl2] 里的 swap、memory,完全没提磁盘大小调整。早期 WSL2 版本确实没有提供类似 wsl --resize-disk <Distro> <Size> 的命令,后续虽然有一些支持的动向,但在稳定的 Windows 10/11 版本上,不少人的 WSL 还停留在 v1 或 v2 的某些老版本。实际上对 Ubuntu 18.04 来说,最稳妥的在线扩容路径是:
code复制wsl --shutdown
然后找到 ext4.vhdx,用 diskpart 对虚拟磁盘进行扩展。但这里有个关键点:WSL2 的根文件系统分区表默认是 GPT,并且最后一个分区(即根分区)往往已经占满了整个虚拟磁盘。 在线扩展虚拟磁盘大小之后,你还需要在 Linux 内部操作分区表和文件系统,而在 WSL2 的默认情况下,系统启动时并不会自动执行分区调整——所以你需要进入一个手动流程:growpart 或 fdisk 删除再重建分区、resize2fs 扩展文件系统。
所以,我认为对新手来说,“在线扩容”这两个字很容易产生误导。你以为运行一条命令就完事了,实际上里面还牵涉虚拟磁盘 VHDX 扩展、分区表调整、文件系统扩增这三轮操作,每一步都有可能出问题。而且,VHDX 文件正被 WSL2 虚拟机进程占用时,Windows 侧的工具往往无法直接修改它,你无论如何都要先做 wsl --shutdown。从实践的角度看,
