这一篇讲什么
磁盘相关的问题,麻烦的往往不是命令不会用,而是命令成功了、结果却不是你以为的那样:删了文件空间没回来、扩了容 df 没变、fstab 看着没错重启却起不来。本篇在三台机器上逐个复现,贴原始输出。
| CentOS 7.9(VMware) | /dev/mapper/centos-root xfs(LVM) | 标准装机 |
| Rocky 9.8(LXD 虚拟机) | /dev/sda2 ext4,没有 LVM | cloud 镜像,和 ISO 安装默认的 xfs + LVM 不一样 |
| Ubuntu 24.04.5(LXD 虚拟机) | /dev/sda2 ext4 | cloud 镜像 |
🔴 所有实验都在 loop 设备上做(truncate 一个文件当磁盘),不碰系统盘。fstab 重启实验只在两台 LXD 虚拟机上做,做完回滚快照。
默认没装的工具(做实验前补装的):
| lsof | 没有 | 没有 | 没有 |
| fuser(psmisc) | 没有 | 有 | 没有 |
| lvm | 有 | 没有 | 没有 |
| growpart | 没有 | 有 | 有 |
| iostat | 没有 | 没有 | 没有 |
1. df 说满了,du 却找不到 —— 文件删了但进程还开着 ✅
在一块 1G 的 ext4 上写 300M 日志,让一个进程一直开着它,然后 rm:
== rm 之前:
/dev/loop0 974 301 607 34% /mnt/lab
== rm 之后:
/dev/loop0 974 301 607 34% /mnt/lab
== du:
1/mnt/lab
(单位 MB,Rocky 9;三台一样。)rm 前后 df 一个字节都没变,du 只剩 1M。 文件从目录里消失了,但进程还拿着它的句柄,空间不会释放。
找出这种「已删除但还被占着」的文件:
# lsof +L1 | grep -E 'COMMAND|/mnt/lab'
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
sleep 9572 root 3w REG 7,0 314572800 0 12 /mnt/lab/big.log (deleted)
NLINK 0 + (deleted) 就是它。不想重启进程的话,通过 /proc 把这个句柄对应的文件清空:
# ls -l /proc/9572/fd | grep deleted
l-wx——. 1 root root 64 Sep 21 17:02 3 -> /mnt/lab/big.log (deleted)
# : > /proc/9572/fd/3
== 清空 /proc/9572/fd/3 之后:
/dev/loop0 974 1 907 1% /mnt/lab
空间立刻回来了。
⚠️ lsof 三台默认都没装。另外 lsof -t +L1 /mnt/lab 这种「后面跟目录」的写法,我实测时拿到的是别的目录下某个已删除文件的进程号,稳妥的写法是不带路径、再 grep。
2. 清空日志用 >,但这里有个手册没写的坑 ✅
「清日志别用 rm,用 > file 或 truncate -s 0」—— 这条是对的,但不完整。结果取决于写日志的程序是怎么打开文件的。
两个进程各写 200M 日志:A 用追加方式(>>)打开,B 用覆盖方式(>)打开。写完后把两个文件都 : > 清空,然后两个进程各再写一行:
== 刚清空:
0 /mnt/lab/a.log
0 /mnt/lab/b.log
== 进程又写了一行之后(ls 看大小 / du 看实际占用):
5 /mnt/lab/a.log
209715205 /mnt/lab/b.log
4/mnt/lab/a.log
4/mnt/lab/b.log
三台一样。追加方式打开的 a.log 正常,只有新写的 5 字节;覆盖方式打开的 b.log,ls 显示又变回 200M 了。
原因:B 进程记着自己写到了第 200M 的位置,文件被清空后它接着在第 200M 处写,前面那一段变成「空洞」。du 显示实际只占 4K(空间确实释放了,df 也回到 1M),但 ls -l 看到的大小是 209715205 字节。
实际影响:清空后 ls 看着像没清掉;拿这个文件去 cat、grep、传到别处,会先读出 200M 的 \\0。Java 应用 nohup java … > app.log 这种写法就是覆盖方式打开的。要让清空干净,启动时用 >>。
3. 「No space left on device」但 df -h 只用了 1% —— inode 用尽 ✅
建一个只有 2048 个 inode 的小 ext4,往里面建空文件:
# df -h /mnt/labi; df -i /mnt/labi (Rocky 9,建文件之前)
/dev/loop1 59M 14K 54M 1% /mnt/labi
/dev/loop1 2048 11 2037 1% /mnt/labi
建到第 2037 个文件停下:touch: cannot touch 'f2038': No space left on device
/dev/loop1 59M 59K 54M 1% /mnt/labi
/dev/loop1 2048 2048 0 100% /mnt/labi
空间只用了 1%,报的却是「没空间」。df -h 看不出来,要看 df -i。
同样建 5000 个文件,在 512M 的 xfs 上毫无压力:
/dev/loop2 262144 3 262141 1% /mnt/labx
xfs 上建了 5000 个
/dev/loop2 262144 5003 257141 2% /mnt/labx
4. df 里 Size ≠ Used + Avail —— ext4 给 root 留了 5% ✅
# df -m /mnt/lab (1G 的 ext4,刚格式化)
/dev/loop0 974 1 907 1% /mnt/lab
# tune2fs -l /dev/loop0 | grep -E '^Reserved block count|^Block count|^Block size'
Block count: 262144
Reserved block count: 13107
Block size: 4096
974 − 1 = 973,但可用只有 907。差的那 66M 左右就是 13107 × 4K 的保留块(5%),普通用户用不了。所以普通用户的程序会在 df 显示 95% 左右时就报满。
5. umount 报 target is busy ✅
umount 退出码=32
umount: /mnt/lab: target is busy.
找是谁占着:
# fuser -vm /mnt/lab
USER PID ACCESS COMMAND
/mnt/lab: root kernel mount /mnt/lab
root 17039 ..c.. sleep
# lsof +D /mnt/lab
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sleep 17039 root cwd DIR 7,0 4096 2 /mnt/lab
ACCESS 里的 c、lsof 里的 cwd 都表示「这个进程的当前目录在里面」—— 最常见的就是你自己的 shell 还 cd 在那。fuser -km 杀掉占用进程后:
fuser -km 之后 umount 退出码=0
⚠️ CentOS 7 和 Ubuntu 默认没有 fuser(在 psmisc 包里)。我第一轮在 CentOS 7 上没装就跑,fuser -km 静默失败,后面的 umount 继续报 busy,还留下一个挂着的 loop 设备要手工清理。
6. fstab:改完先验证,再重启 ✅
6.1 mount -a 能发现什么
往 fstab 里加两行:一行是真实的 loop 盘,一行是一个不存在的 UUID:
UUID=84b369df-7dab-4607-aa31-d4b4d2d1db70 /mnt/lab ext4 defaults 0 0
UUID=00000000-dead-beef-0000-000000000000 /mnt/labgone ext4 defaults 0 0
# mount -a; echo "mount -a 退出码=$?" (Rocky 9)
mount -a 退出码=64
mount: /mnt/labgone: can't find UUID=00000000-dead-beef-0000-000000000000.
CentOS 7 的提示是 mount: can't find UUID=00000000-dead-beef-0000-000000000000。
6.2 findmnt –verify:CentOS 7 没有
# Rocky 9
/
[W] recommended root FS passno is 1 (current is 0)
/mnt/labgone
[E] unreachable on boot required source: UUID=00000000-dead-beef-0000-000000000000
findmnt –verify 退出码=1
0 parse errors, 1 error, 1 warning
# Ubuntu 24.04 多一条
[W] your fstab has been modified, but systemd still uses the old version;
use 'systemctl daemon-reload' to reload
# CentOS 7
findmnt –verify 退出码=1
findmnt: unrecognized option '–verify'
[E] unreachable on boot required source 这句说得最直白:这个设备开机时找不到,而且它是必需的。Ubuntu 还会提醒:改了 fstab 要 systemctl daemon-reload,否则 systemd 还按旧的来(systemd 会把 fstab 转成 mount 单元)。
([W] recommended root FS passno is 1 是 cloud 镜像自带 fstab 的写法,和本实验无关。)
6.3 加 nofail 之后
加 nofail 后 mount -a 退出码=0 (Rocky 9 / Ubuntu)
加 nofail 后 mount -a 退出码=32 (CentOS 7)
mount: can't find UUID=00000000-dead-beef-0000-000000000000
注意 CentOS 7 上即使加了 nofail,mount -a 照样报错、退出码非 0。
6.4 少写一列,不是语法错误
/mnt/typo /mnt/lab2 ext4 defaults 0 ← 少了最后一列
少一列 mount -a 退出码=64
mount: /mnt/lab2: mount point does not exist.
报的是挂载点不存在,不是「格式错了」。fstab 最后两列可以省略,少一列本身不算错 —— 真正的问题要看提示。
7. fstab 写错了会怎样:重启实测 ✅(只在 Rocky 9 / Ubuntu 上做)
7.1 不加 nofail:进紧急模式,SSH 连不上
fstab 里留着那行不存在的 UUID(不带 nofail),重启。3 分钟后从宿主机探测:
10.115.180.175:22 不通
10.115.180.200:22 不通
rocky9,RUNNING,1,10.115.180.175 (eth0)
ubuntu2404,RUNNING,1,10.115.180.200 (eth0)
虚拟机在运行,但 SSH 不通。接上串口控制台看:
Give root password for maintenance
(or press Control-D to continue):
两台都停在这里 —— 这就是紧急模式(emergency mode),在等 root 密码。我在这里按了一下回车(空密码),得到的是 Login incorrect。
如果这是一台只能 SSH 进去的云服务器,到这一步就只能去云控制台开 VNC 了。 本次两台都是回滚快照恢复的。
7.2 加了 nofail:能开机,但启动多等了 90 秒
同一行加上 nofail,再重启:
5s rocky=down ubuntu=down
10s rocky=up ubuntu=up
10 秒就能 SSH 上去了。但此时:
Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=0).
Please try again later.
starting
系统还没「启动完成」。重新做一次同样的重启,这次等 150 秒后再看:
# Rocky 9
Startup finished in 523ms (kernel) + 1.221s (initrd) + 1min 30.209s (userspace) = 1min 31.953s
running
/mnt/labgone 没挂上
Timed out waiting for device /dev/disk/by-uuid/00000000-dead-beef-0000-000000000000.
Dependency failed for /mnt/labgone.
# Ubuntu 24.04
Startup finished in 849ms (kernel) + 1min 30.234s (userspace) = 1min 31.083s
running
/mnt/labgone 没挂上
nofail 保证能开机,但 systemd 仍然会等这块盘 90 秒才放弃。依赖「启动完成」的东西(比如等 multi-user.target 之后才执行的服务)会被拖慢一分半。所以 nofail 是保险,不是修复 —— 盘不在了就把那行注释掉。
8. LVM 扩容:两步,少一步 df 不变 ✅
建一个卷组,里面一个 xfs 逻辑卷(500M)、一个 ext4 逻辑卷(300M),然后给卷组加一块盘,把 xfs 那个扩 400M:
# lvextend -L +400M /dev/vglab/lvx (Rocky 9)
Logical volume vglab/lvx successfully resized.
== lvs:
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
lvx vglab -wi-ao—- 900.00m
== df:
/dev/mapper/vglab-lvx 436M 29M 408M 7% /mnt/lvx
lvs 显示 900M,df 还是 436M —— 逻辑卷扩了,文件系统没扩。第二步:
# xfs_growfs /mnt/lvx
data blocks changed from 128000 to 230400
/dev/mapper/vglab-lvx 836M 32M 805M 4% /mnt/lvx
三台一样。几个细节:
- xfs_growfs 传挂载点、传设备名(/dev/vglab/lvx)都能用,实测退出码都是 0。
- 用 lvextend -r 一步到位,它会自己调 resize2fs / xfs_growfs:
# lvextend -r -L +200M /dev/vglab/lve (CentOS 7,ext4,在线扩)
resize2fs 1.42.9 (28-Dec-2013)
Filesystem at /dev/mapper/vglab-lve is mounted on /mnt/lve; on-line resizing required
old_desc_blocks = 3, new_desc_blocks = 4
The filesystem on /dev/mapper/vglab-lve is now 512000 blocks long.
8.1 缩容:xfs 不行,ext4 可以(挂着也行)
# lvreduce -y -r -L -100M /dev/vglab/lvx (xfs)
fsadm: Xfs filesystem shrinking is unsupported. (CentOS 7 / Ubuntu)
File system reduce is required and not supported (xfs). (Rocky 9)
xfs 缩容 退出码=5
逻辑卷大小没变,数据没事 —— 工具拦下来了。xfs 只能扩不能缩,规划分区时就要想好。
ext4 在挂载状态下缩:
# lvreduce -y -r -L -100M /dev/vglab/lve (Rocky 9)
remount done
Reduced file system ext4 on vglab/lve.
Size of logical volume vglab/lve changed from 500.00 MiB (125 extents) to 400.00 MiB (100 extents).
Logical volume vglab/lve successfully resized.
ext4(已挂载)缩容 退出码=0
三台都成功了。很多资料说「ext4 缩小必须先卸载」—— 缩容这个动作本身确实要卸载,但 lvreduce -r 会自己卸载、缩、再挂回去(Rocky 9 的输出里能看到 remount done)。前提是它卸得下来;有进程占着的情况我没测。缩容有丢数据风险,先备份。
9. 云盘扩容:盘大了,分区和文件系统没大 ✅
云厂商控制台里把磁盘从 1G 扩到 2G 之后,系统里的样子(用一个带 GPT 分区表的 loop 文件模拟):
== 盘扩到 2G 之后:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 2G 0 loop
└─loop0p1 259:0 0 511M 0 part /mnt/labg
/dev/loop0p1 447M 29M 419M 7% /mnt/labg
盘 2G 了,分区还是 511M,df 还是 447M。 要三步:盘 → 分区 → 文件系统。
# growpart /dev/loop0 1
CHANGED: partition=1 start=2048 old: size=1046528 end=1048575 new: size=4192223 end=4194270
growpart 退出码=0
# xfs_growfs /mnt/labg
data blocks changed from 130816 to 524027
/dev/loop0p1 2.0G 41M 1.9G 3% /mnt/labg
(ext4 的话第三步换成 resize2fs /dev/分区。)
⚠️ CentOS 7 上 growpart 在 cloud-utils-growpart 包里,默认没装;装了还不够,GPT 分区还要 gdisk。第一轮没装 gdisk 时:
FAILED: failed to get a resizer for id ''
growpart 退出码=2
no tools available to resize disk with 'gpt'
装上 gdisk 后同一条命令成功。Rocky 9 和 Ubuntu 的 cloud 镜像自带 growpart,直接能用。
10. fsck:挂着的时候别跑 ✅
# e2fsck -f /dev/vglab/lve (已挂载)
/dev/vglab/lve is mounted.
e2fsck: Cannot continue, aborting.
已挂载时 e2fsck 退出码=8
# xfs_repair -n /dev/vglab/lvx (已挂载,-n 只检查不修改)
xfs_repair: /dev/vglab/lvx contains a mounted and writable filesystem
已挂载时 xfs_repair -n 退出码=1
三台都拒绝执行。修文件系统要先卸载;根分区只能进救援模式。
11. 手册(和很多教程)里需要改的地方
| 清日志用 > file 就能释放 | 空间确实释放,但程序若用覆盖方式(>)打开文件,清空后再写一行,ls 又显示原来的大小(稀疏文件) |
| 改完 fstab 用 findmnt –verify 检查 | CentOS 7 没有这个选项 |
| 加了 nofail 就没事了 | 能开机,但启动多等 90 秒;CentOS 7 上 mount -a 照样报错 |
| ext4 缩小必须先卸载 | lvreduce -r 会自己卸载再挂回,挂着执行三台都成功 |
| xfs_growfs 要传挂载点 | 传设备名也能用 |
12. 速查
磁盘满了:
df -h; df -i # 空间满还是 inode 满
du -h –max-depth=1 / 2>/dev/null | sort -rh | head
lsof +L1 | grep deleted # df 满 du 不满:被删但还开着
: > /proc/<PID>/fd/<FD> # 不重启进程释放空间
扩容:
# LVM
lvextend -r -L +10G /dev/vg/lv # -r 连文件系统一起扩
# 云盘(非 LVM)
growpart /dev/sda 1 # CentOS 7 要装 cloud-utils-growpart + gdisk
xfs_growfs /挂载点 或 resize2fs /dev/sda1
改 fstab:
blkid /dev/xxx # 用 UUID
mount -a # 退出码非 0 就别重启
findmnt –verify # 7 上没有
systemctl daemon-reload # 改完 fstab 让 systemd 读新的
下一篇:网络配置与 SSH —— nmcli、netplan、DNS 被覆盖、SSH 改端口、密钥登录被拒。
网硕互联帮助中心






评论前必须登录!
注册