ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

WSL2虚拟磁盘迁移:解决C盘空间不足,将ext4.vhdx搬到D盘

2026/9/20 4:36:17 拓冰建站 浏览量
WSL2虚拟磁盘迁移:解决C盘空间不足,将ext4.vhdx搬到D盘 上周又有一位同事的C盘一路飙红排查到最后锅全在WSL的虚拟磁盘上。这几乎是每个重度使用WSL的人迟早要面对的问题默认装好的发行版把整个 ext4.vhdx 扔在C盘初始只有几百MB随着你 apt install、装conda、拉Docker镜像它会一路膨胀到几十个GB而且删文件也不会瘦回去。这篇文章就专门解决WSL迁移磁盘位置这件事把C盘上的发行版整体搬到D盘或其它分区思路和步骤都能直接照着抄适合C盘吃紧、想把开发环境与数据盘分离的开发者参考。1. 为什么C盘会被WSL2悄悄塞满这个问题非解决不可很多人第一次意识到WSL占空间不是因为主动去看而是因为收到“磁盘空间不足”的红色告警。WSL2本身不是一个简单的“Linux兼容层”它跑在一个轻量级虚拟机里整个Linux文件系统都被封装进一个叫 ext4.vhdx 的虚拟磁盘文件中。这个文件默认放在用户目录下具体路径形如C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\ext4.vhdx不同版本的Ubuntu或者其它发行版包名会不一样。想快速找到这个文件我一般直接在PowerShell里跑一条递归查找命令Get-ChildItem -Path $env:LOCALAPPDATA\Packages -Filter ext4.vhdx -Recurse | Select-Object DirectoryName, {NSizeGB;E{[math]::Round($_.Length / 1GB, 2)}}看到结果你会明白占空间的不是所谓“WSL程序本身”而是这一个文件。真正可怕的地方在于vhdx是动态扩展的。你可以把它理解成一张“额度不设上限”的信用卡用多少记多少但你还了钱额度却不会降回去。你在WSL里安装apt包、conda环境、npm包甚至是Docker镜像层都会让这个文件变大可是当你apt remove或者删除项目目录时文件却不会自动缩小。空闲块被标记为可复用但物理上并没有归还给Windows。更现实的情况是现代开发环境里C盘从来不只是系统盘——VSCode的扩展缓存、Docker Desktop的镜像、浏览器缓存、微信文件全在C盘WSL2的vhdx再过来插一脚C盘的可用空间就是被这样一点点吃光的。所以我常说迁移WSL磁盘位置不是“可选优化”而是很多开发者的刚需。尤其是那些用WSL2跑Docker Desktop的人lhdx文件涨到20GB、30GB都很常见甚至我见过有人C盘爆掉的原因就是一个40多GB的ext4.vhdx。这个章节先把问题背景说透接下来进入正题迁移之前到底要做什么准备。2. 迁移前必须确认的三件事备份、查占用、改设置动WSL之前别急着敲命令。迁移流程本身不算复杂但准备工作偷懒后面大概率要返工。我通常按下面四步走。2.1 列出发行版列表确认WSL版本先打开PowerShell或Windows Terminal执行wsl -l -v输出大致是这样NAME STATE VERSION * Ubuntu Running 2重点看两处发行版的“真实名称”和VERSION列。VERSION必须是2本文的迁移流程主要针对WSL2。如果你还在用WSL1建议先升级到WSL2再说因为WSL1没有vhdx文件迁移方式完全不是一回事。这里还有个小坑wsl -l -v显示的NAME可能带星号星号代表默认发行版。后面执行wsl --export时千万别把星号也复制进去。想拿到干净的发行版名可以执行wsl -l -q它会只输出一行名称比如Ubuntu或Ubuntu-22.04复制这个结果更保险。2.2 统计WSL内部的实际占用备份之前最好先知道WSL里到底装了多少东西。进入WSL执行df -h这会列出Linux文件系统的挂载信息和已用空间。但要注意df看到的是Linux视角的磁盘占用和Windows侧vhdx文件大小并不完全一致。比如Linux里显示用了18GBvhdx在Windows里可能已经膨胀到20GB这是因为文件系统元数据、稀疏块、未回收的已删除数据都会占空间。想进一步排查具体是哪个目录在吃空间可以用sudo du -h --max-depth1 /home/你的用户名 | sort -h重点看~/下的隐藏目录比如~/.conda、~/.cache、~/.vscode-server、~/.docker这几个都是经典的体积大户。了解了数据构成迁移完之后的清理工作也更有针对性。2.3 关闭所有WSL实例和相关程序执行wsl --shutdown这会立即终止所有正在运行的WSL发行版和WSL2虚拟机。需要注意Docker Desktop如果开着它内部使用的WSL2后端会把vhdx文件继续锁住光执行wsl --shutdown不一定能完全释放文件句柄。所以也可以在任务管理器里确认一下右键结束 Docker Desktop 以及Docker Desktop Backend相关进程或者干脆先退出Docker Desktop再执行shutdown。判断vhdx是否真的被释放有一个笨方法迁移前用PowerShell执行wsl --shutdown等几秒然后尝试把ext4.vhdx文件重命名。如果提示“文件被另一个进程占用”说明还有进程没退干净重命名成功则说明锁已经释放。2.4 创建备份文件给迁移兜底迁移最怕的是数据丢失。虽然导出导入的方式本身不会碰原数据但谁也不敢保证不会出现断电、磁盘损坏这种意外。所以我会习惯性先导出一份tar备份wsl --export Ubuntu D:\wsl_backup\Ubuntu_backup.tar这里有几个经验点导出文件的存放位置最好和目标盘一致或者放到另一个空闲盘不要放在C盘。因为导出过程中tar包会占用与vhdx实际数据差不多大的空间C盘本来就紧张再塞一个大tar包容易造成空间耗尽。导出的耗时取决于数据量通常5到15分钟都正常不要中途强行关闭终端。如果你想更稳妥可以在导出完成后看一眼tar文件的大小和df -h里的已用空间做对比确认导出内容基本完整。准备工作做完下面进入真正的迁移环节。3. 两条路子官方导出导入法与vhdx文件直移法迁移WSL磁盘位置社区里最常见的做法其实就两种一种是走wsl --exportwsl --import的官方导出导入流程另一种是更快更激进的vhdx文件直接移动法。两种方式各有适用场景我分别讲清楚。3.1 方案Awsl --export wsl --import这是官方支持度最高、也最“正统”的方式。它把整个Linux文件系统打包成tar然后在指定位置重新生成vhdx。完整命令序列如下# 1. 关闭所有WSL实例 wsl --shutdown # 2. 导出发行版到备份目录 wsl --export Ubuntu D:\wsl_backup\Ubuntu.tar # 3. 注销原发行版会删除原vhdx注册信息但备份tar还在 wsl --unregister Ubuntu # 4. 在目标盘创建新目录 mkdir D:\WSL\Ubuntu # 5. 从tar导入到新位置并指定WSL版本为2 wsl --import Ubuntu D:\WSL\Ubuntu D:\wsl_backup\Ubuntu.tar --version 2执行完最后一步WSL会自动在D:\WSL\Ubuntu目录下生成一个新的ext4.vhdx并在Windows侧登记这个发行版。验证方式也很简单wsl -l -v看到Ubuntu的STATE变为Stopped、VERSION为2基本就成功了。这套流程的关键点有两个。第一--unregister会删除原发行版的注册信息和原vhdx文件但因为它不会动备份tar所以数据是安全的。这也是为什么我强烈建议导出成功后再执行unregister哪怕导出多花点时间也值得。第二--version 2参数必须带否则老版本WSL可能默认生成WSL1格式路径挂载行为会发生变化。3.2 方案Bimport-in-place 直接注册vhdx如果你已经比较确定原vhdx文件没有问题不想再花时间导出再导入可以用新版本WSL提供的--import-in-place命令。它的作用是把一个现成的vhdx文件直接登记为新发行版不再复制数据。整体思路是先复制vhdx到目标盘注销旧发行版再注册新位置的vhdx。具体步骤# 1. 关闭所有WSL实例 wsl --shutdown # 2. 找到原vhdx路径 # C:\Users\你的用户名\AppData\Local\Packages\...\LocalState\ext4.vhdx # 3. 复制vhdx到新目录先复制别剪切验证成功再删原文件 mkdir D:\WSL\Ubuntu copy C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\ext4.vhdx D:\WSL\Ubuntu\ext4.vhdx # 4. 注销原发行版 wsl --unregister Ubuntu # 5. 用vhdx文件直接注册新发行版 wsl --import-in-place Ubuntu D:\WSL\Ubuntu\ext4.vhdx注意--import-in-place的第二个参数是vhdx文件的完整路径不是目录。这个命令对WSL版本有要求运行wsl --version可以查看如果提示无法识别命令说明版本太旧老老实实走方案A。这种方式的优点是少一次tar打包解包速度快很多。缺点也明显复制期间一旦中断vhdx文件可能损坏而你又没有tar备份兜底。所以我个人的建议是如果你手里已经有tar备份或者数据本身不重要可以优先试试方案B如果是第一次迁移、数据又比较珍贵老老实实走方案A。3.3 两个方案的对比对比项方案Aexport import方案Bimport-in-place额外磁盘空间需要一个tar包的空间需要复制一份vhdx的空间耗时导出导入较长复制注册较短备份安全性高tar本身就是备份中依赖复制过程不出错WSL版本要求低几乎所有版本都行高需要较新WSL适合场景首次迁移、数据重要、想顺便备份数据量小、只求快速迁移、已有其它备份两种办法我都在实际项目中用过结论是方案A适合“求稳”的场景方案B适合“求快”的场景。如果你连vhdx文件都找不到还是先花力气把路径弄清楚再考虑用哪种方案。4. 迁移后的收尾工作恢复默认用户、路径和工具链验证迁移完成后别急着欢呼。导入的发行版虽然能启动但很多原始配置不会自动恢复最典型的就是默认用户变成了root。这一步如果不处理后面每个操作都很别扭。4.1 恢复默认用户无论是方案A还是方案B新注册的发行版默认都会以root身份登录。想恢复成原来的普通用户有两种办法。第一种是新版WSL提供的命令最省事wsl --manage Ubuntu --set-default-user 你的用户名第二种是修改WSL内的配置文件。先以root进系统然后查一下原用户名wsl -d Ubuntu -u root ls /home拿到用户名后编辑/etc/wsl.confsudo nano /etc/wsl.conf在文件里追加[user] default你的用户名保存后执行wsl --terminate Ubuntu再次进入WSL运行whoami如果输出你的普通用户名就代表恢复成功了。提示如果你在ls /home里发现用户目录还在只是默认登录用户变了完全不用担心数据丢失。这只是登录身份问题不是数据被清空。4.2 检查挂载、DNS和主机名接下来检查几个最容易在迁移后出问题的系统级配置。先看Windows盘符挂载ls /mnt/c如果/mnt/c下面能看到Windows文件说明自动挂载正常。如果提示不存在或空目录需要在/etc/wsl.conf中确保配置了[automount] enabledtrue再看DNS。迁移不会重置/etc/resolv.conf但如果你之前手动改过这个文件可以确认一下内容还在不在。被覆盖了也不用慌按原方式重新配置即可。还有hostname。某些版本的WSL在导入后hostname会变化这会导致运行中的服务、SSH连接、一些依赖主机名的脚本出现异常。执行一下hostname检查如果变了但你又想保持一致可以按自己的方式改回来。4.3 VSCode和Docker Desktop联动验证对于大多数开发者迁移完最重要的事不是跑服务而是让开发工具重新识别WSL。VSCode这边只要Remote-WSL插件还装着通常会在WSL里自动重新下载/复用~/.vscode-server。验证方式在WSL终端里输入code .如果能弹出VSCode窗口并自动进入WSL模式说明联动正常。如果提示找不到code命令检查PATH里是否包含VSCode的bin目录路径形如/mnt/c/Users/你的用户名/AppData/Local/Programs/Microsoft VS Code/binDocker Desktop则需要额外关心。打开Docker Desktop的 Settings - Resources - WSL Integration确认你的发行版名称在列表里并且开关是打开的。如果发行版名称和原来不一致比如你导入时改了个名字这里必须重新勾选。选完后重启Docker Desktop进入WSL执行docker ps能正常返回容器列表就说明连通了。这里要特别说明Docker Desktop在WSL2模式下本身还会创建两个内部发行版docker-desktop和docker-desktop-data它们默认也在C盘。你迁移的是自己的Ubuntu发行版并不会迁移Docker Desktop的数据。如果C盘空间问题主要是Docker镜像引起的迁Ubuntu发行版可能解决不了根本问题还得单独处理Docker Desktop的Disk image location这是另外一个话题但你要有这个概念。4.4 业务数据与服务状态核查最后一步把迁移前正在跑的东西全部拉起来验证一遍。比如数据库、nginx、Redis这类服务启动后检查端口是否正常监听systemctl status mysql ss -tlnp | grep 3306还有Git配置、SSH密钥这些“软配置”虽然它们都存在文件系统里跟着迁移走了但如果你之前设置过基于原hostname的路径引用可能也需要微调。这些细节很碎但每一条都能避免后面“用着用着发现不对劲”的尴尬。5. 迁移过程中最容易翻车的几个环节我的排查思路这一章我不讲标准操作专门讲讲我实际踩过的坑和帮别人排查时见过的典型问题。如果你在迁移中遇到类似情况照着这个思路走能省不少时间。5.1 导出时报错“找不到发行版名称”第一次执行wsl --export时最容易犯的错是发行版名称写错。wsl -l -v的输出带了星号和空格直接复制粘贴很容易把星号带上。建议执行wsl -l -q拿纯名称。另外注意发行版名称是区分大小写的ubuntu和Ubuntu在部分版本的WSL里会被当成两个不同名字。碰到无法解析的报错时先把名称原样拷贝到记事本再执行导出命令。5.2 导入后发现默认用户是root这个我在第4章说了属于正常现象不是数据丢失。但有个容易让人误判的点如果你以root登录后执行ls /home发现原用户的目录还在但文件显示的所有者可能变成数字ID。这是因为tar包导出导入时UID/GID映射在极少数情况下会错乱。遇到这种情况先执行chown -R 原用户名:原用户名 /home/原用户名修正所有者即可。我自己遇到过不止一次。修复完所有权后还要检查一下sudo是否还能用因为/etc/sudoers里的用户组配置也可能需要调整。5.3 unregister 之后C盘空间没变小有人反馈说执行完wsl --unregister UbuntuC盘可用空间并没有立刻增大。这不一定是迁移失败更可能是以下两个原因之一第一Docker Desktop的数据没跟着迁移它自己有一个docker-desktop-data发行版vhdx文件依然在C盘。想知道是不是它在占空间直接找一下这些路径C:\Users\你的用户名\AppData\Local\Docker\wsl\data\ext4.vhdx C:\Users\你的用户名\AppData\Local\Docker\wsl\distro\ext4.vhdx这两个文件很容易就是20GB起步。第二Windows的卷影副本或者其他磁盘管理机制可能让空间暂时没有“秒释放”。这时候不用急过几分钟再看或者用磁盘清理工具观察空间变化。如果确实一直没变用资源监视器看看是否有进程还在占用原vhdx文件句柄。5.4 导入后启动失败提示WSL2内核需要更新这种问题通常不是迁移本身造成的而是因为目标机器上的WSL内核版本太老无法识别新位置上的vhdx文件。解决办法是先执行wsl --update如果因为网络原因导致更新失败比如一些公司网络会拦截微软更新通道可以手动下载WSL安装包离线安装。这属于WSL版本管理的问题与迁移无直接关系但很容易在迁移完成后的第一次启动时冒出来让人误以为迁移搞砸了。5.5 Docker Desktop 显示“无法找到发行版”打开Docker Desktop后发现WSL Integration列表里没有你的发行版第一反应不是去重装Docker Desktop而是确认发行版名称是否变化。如果你在导入时用了新名称比如从Ubuntu改成UbuntuMigratedDocker Desktop不会自动感知新名称需要在设置里重新勾选。如果名称没变还是不显示尝试把Docker Desktop彻底退出后重启一次它会重新扫描WSL发行版列表。5.6 迁移后 Windows Terminal 的配置还在吗Windows Terminal的配置文件存储在Windows用户目录下不在WSL文件系统里所以迁移对它没有影响。只要发行版名称没变Terminal里的Ubuntu配置会自动落到新发行版上。这也说明了一个经验导出导入时尽量保持发行版名称不变能少改很多东西。6. 让虚胖的vhdx瘦回去迁移之外的磁盘空间管理技巧迁移完成后你以为C盘问题就一劳永逸了如果不对vhdx做定期维护过几个月它又会在D盘慢慢膨胀。这一章分享一些我在实操中验证过的维护手段。6.1 为什么删文件后vhdx不会缩小前面说过Linux删除文件只是把对应的数据块标记为“可复用”Windows侧的vhdx并不会感知这些块已经空闲。除非WSL主动把空闲块信息通过TRIM指令告诉Windows否则虚拟磁盘文件始终保留着历史最大体积。要真正让vhdx瘦下来需要三步先清理Linux内部不需要的文件再让文件系统标记空闲块最后压缩vhdx。清理文件命令示例sudo apt clean sudo apt autoremove -y pip cache purge conda clean --all -y docker system prune -a --volumes # 慎用会删除所有未使用的镜像和卷清理完之后在WSL里执行sudo fstrim -av这一步会把可回收的块信息提交给宿主系统。然后关闭WSLwsl --shutdown最后用diskpart压缩vhdx。打开管理员PowerShell新建一个临时目录执行diskpart select vdisk fileD:\WSL\Ubuntu\ext4.vhdx attach vdisk readonly compact vdisk detach vdisk exit压缩完成后再看vhdx文件大小通常会小不少。如果你安装了Hyper-V管理工具也可以用等价命令Optimize-VHD -Path D:\WSL\Ubuntu\ext4.vhdx -Mode Full注意压缩vhdx期间WSL必须处于关机状态。我见过有人忘了wsl --shutdown就执行compact结果diskpart直接报错文件被占用。6.2 用 .wslconfig 把 swap 也挪到D盘很多人不知道WSL2默认会在C盘创建swap文件路径通常在C:\Users\你的用户名\AppData\Local\Temp\swap.vhdx这个文件默认大小和内存配置有关默认情况下占几个GB很常见。想让它也离开C盘可以在用户目录下创建或编辑.wslconfig文件[wsl2] memory8GB processors4 swap4GB swapFileD:\\wsl\\swap.vhdx localhostForwardingtrue注意.wslconfig文件位于C:\Users\你的用户名\.wslconfig是Windows侧的文件不是WSL内的文件。修改后执行wsl --shutdown再重新进入WSL才生效。这里有个经验memory和swap不要设置过大。不是机器内存不够而是这两个值直接影响swap.vhdx和WSL内存占用设太大会在C盘或D盘多占不必要的空间。一般开发场景8GB足够编译大型C项目或者训练模型时再临时调高。6.3 把大体积数据放到Windows盘别全塞进WSL内部迁移完成后我建议你做个长期规划哪些东西必须放在WSL的Linux文件系统里哪些可以放在Windows盘通过/mnt/d访问。一个很典型的例子是深度学习模型权重动不动几个GB甚至几十GB放在WSL内部会撑大vhdx。放在/mnt/d/models下需要用时用软链接指过去即可mkdir -p /mnt/d/projects/大体积数据 ln -s /mnt/d/projects/大体积数据 ~/projects/data这种做法的代价是跨文件系统IO会有一些额外开销对读写频率极高的场景不太友好但对模型文件、数据集、软件安装包这种“读多写少”的内容非常合适。反过来node_modules这种成千上万个文件的目录放Windows侧会出现明显的IO性能下降我建议还是保留在WSL内部。6.4 养成定期清理的好习惯迁移不是终点是新的起点。我会在每个月末固定做一次WSL体检流程很简单查df -h查vhdx文件大小跑一次fstrim如果发现膨胀超过预期再走一遍diskpart压缩。还有一个平时很好用的习惯用du定期扫描大目录找出那些“装完就忘”的安装包缓存。比如sudo du -h --max-depth1 /var 2/dev/null | sort -h | tail -20 sudo du -h --max-depth1 /home/你的用户名 2/dev/null | sort -h | tail -20看到异常目录后顺手清理能避免很多C盘/D盘告警的麻烦。我个人在实际操作中的体会是WSL迁移磁盘位置这件事真正的难点从来不是敲那几条命令而是迁移前想清楚数据构成、迁移后查漏补齐。很多人只盯着“C盘到D盘”这一层结果迁完发现Docker Desktop的数据还在C盘白白跑了一趟。先用du摸清WSL内部体积再决定关键是迁发行版还是连Docker数据一起处理这才是高效的做法。最后再分享一个小技巧迁移完成后如果发现新位置的vhdx在运行一段时间后又迅速膨胀不妨在WSL里检查一下是否有服务在持续写日志或缓存。把日志重定向到/mnt/d/logs或者合理配置日志轮转比事后压缩省心得多。这套流程我前前后后帮同事跑了不下十次照着走基本不会出岔子。