最近这台 ImmortalWrt x86 路由器出现了明显的空间问题,根分区 / 已经被打满,nlbwmon 开始报 No space left on device,uhttpd 也出现了 Bus error。一开始看起来像是“硬盘太小”,但实际检查后发现并不是磁盘容量不够,而是系统根分区本身只分到了很小的一块空间。
这篇文章记录一下我是怎么把这台 ImmortalWrt 24.10.5 的根分区从 300M 扩到 5G 的,以及过程中踩到的坑。
问题现象
先看根分区状态:
1 | df -h |
当时的输出核心信息是:
1 | Filesystem Size Used Available Use% Mounted on |
看起来像是系统盘很小,但实际磁盘并不小。继续查看磁盘分区:
1 | fdisk -l /dev/sda |
实际结果类似这样:
1 | Disk /dev/sda: 238.47 GiB |
也就是说:
- 整块硬盘有
238G /boot是32M- 根分区
/dev/sda2只有300M - 后面还有大量未分配空间
根因到这里就很明确了:不是硬盘小,而是系统安装时根分区就只做了 300M。
扩容前一定先备份
这种操作涉及分区表和文件系统,第一步一定不是改盘,而是备份。
我先在路由器上导出了配置和关键信息,再拉回本地机器:
1 | sysupgrade -b /tmp/sysupgrade-backup.tar.gz |
然后把这些文件保存到本机工作目录里,避免路由器后续操作失败时没有回滚依据。
先扩分区,再扩文件系统
扩容不能上来就 resize2fs,因为那时候分区本身还是 300M。正确顺序是:
- 先把
/dev/sda2分区扩到目标大小 - 再把
ext4文件系统扩到新分区上限
这台机器的 /dev/sda2 是磁盘最后一个有效分区,后面全是空闲空间,所以非常适合直接扩展。
先用 parted 调整分区:
1 | parted -s /dev/sda unit MiB print free |
这里 5152MiB 的意思是把第 2 个分区的结束位置扩到大约 5GiB。
调整后的结果类似这样:
1 | Number Start End Size |
之后重启一次,让内核重新读取新的分区表。
第一个坑:在线 ext4 扩容失败
分区扩好后,先尝试常规做法:
1 | resize2fs /dev/sda2 |
理论上很多 ext4 场景支持在线扩容,但这台 ImmortalWrt x86 并没有顺利完成,报错大意是:
1 | resize2fs: Invalid argument |
dmesg 里也能看到类似信息,说明不是命令写错,而是这套 ext4 元数据本身不适合直接在线扩到目标大小。
当时文件系统虽然有轻微变化,但并没有真正扩到 5G,依然停留在很小的空间上。
第二个坑:系统里缺少 tune2fs
继续排查后发现,真正要解决这个 ext4 问题,需要先处理 resize_inode 相关元数据,而这个步骤要依赖 tune2fs。
但这台机器虽然装了 e2fsprogs,却没有 tune2fs 命令:
1 | tune2fs: not found |
所以后面先安装:
1 | opkg update |
这个坑比较关键。如果没有 tune2fs,后续“看起来像在修 ext4”的操作,其实只是做了不完整的一半。
实际可行的做法:两阶段扩容
因为这是路由器,不能长时间手工守在单用户模式里,所以我最后采用的是两阶段做法:
第一阶段:修 ext4 元数据并执行文件系统检查
第一阶段脚本做这些事情:
1 | mount -o remount,ro / |
这里的重点是:
- 把根文件系统重新挂成只读
- 用
tune2fs去掉resize_inode - 用
e2fsck修复 ext4 元数据 - 然后强制重启
实际日志里可以看到 e2fsck 确实修复了相关问题,例如:
1 | Filesystem does not have resize_inode enabled, but s_reserved_gdt_blocks is 23; should be zero. Fix? yes |
第二阶段:开机后自动执行 resize2fs
第一阶段完成并重启后,再由开机脚本执行:
1 | resize2fs /dev/sda2 |
这一次就成功了,输出类似:
1 | The filesystem on /dev/sda2 is now 1310656 (4k) blocks long. |
之后再看磁盘空间:
1 | df -h / |
结果变成:
1 | Filesystem Size Used Available Use% Mounted on |
扩容到这里就真正完成了。
最终验证
扩容完成后,我额外做了这些验证:
检查根分区大小
1 | fdisk -l /dev/sda |
确认:
/dev/sda2分区已经是5G/文件系统已经增长到约4.9G
检查网络和服务
1 | /etc/init.d/dropbear status |
结果都正常,说明这次扩容没有把 SSH、网络和核心服务带坏。
扩容后的一个附带问题
因为过程中做过强制重启,/boot 的 FAT 分区被标记成了未正常卸载。只读检查结果是:
1 | fsck.fat -n -v /dev/sda1 |
核心信息为:
1 | Dirty bit is set. Fs was not properly unmounted and some data may be corrupt. |
这说明:
- FAT 文件系统结构本身没有发现明显损坏
- 只是脏标记被置位
- 如果要真正清掉这个标记,需要在
/dev/sda1未挂载时再跑一次修复模式的fsck.fat
这次扩容的关键经验
这次最重要的经验其实就三条:
1. 先确认是不是“分区太小”,不要只看 df -h
df -h 只能告诉你文件系统满了,不能告诉你底层磁盘布局到底是什么样。像这次问题,真正的根因是 238G 硬盘只给了根分区 300M。
2. OpenWrt/ImmortalWrt x86 的 ext4 扩容,不一定能直接在线成功
如果看到 reserve_backup_gdb、error (-22) 这类信息,不要在同一个思路上反复硬试。先去看 ext4 元数据问题,而不是只会重复执行 resize2fs。
3. 缺工具时先补齐环境
如果系统里没有 tune2fs,那就先装,不要幻想能靠“差不多的命令”糊过去。很多时候不是方案错了,而是执行条件根本没满足。
总结
这次扩容的本质不是“把一个目录清一清”,而是:
- 识别出根分区安装得过小
- 先扩 GPT 分区
- 再修 ext4 元数据
- 最后完成文件系统扩容
最终结果是:
- 根分区从
300M提升到5G - 实际根文件系统可用空间提升到
4.6G - 系统、SSH、网络和核心服务都正常恢复
如果你也在 ImmortalWrt/OpenWrt x86 上遇到 / 爆满,但实际硬盘很大的情况,优先检查分区布局。很多时候问题不在磁盘本身,而在安装镜像时只给了一个很小的根分区。