Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查

这一篇讲什么

磁盘相关的问题,麻烦的往往不是命令不会用,而是命令成功了、结果却不是你以为的那样 :删了文件空间没回来、扩了容 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 虚拟机上做,做完回滚快照。

默认没装的工具(做实验前补装的):

CentOS 7 Rocky 9 Ubuntu 24.04
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. 速查

磁盘满了:

bash 复制代码
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>                       # 不重启进程释放空间

扩容:

bash 复制代码
# 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:

bash 复制代码
blkid /dev/xxx                                # 用 UUID
mount -a                                      # 退出码非 0 就别重启
findmnt --verify                              # 7 上没有
systemctl daemon-reload                       # 改完 fstab 让 systemd 读新的

下一篇:网络配置与 SSH ------ nmcli、netplan、DNS 被覆盖、SSH 改端口、密钥登录被拒。

相关推荐
蓝胖的四次元口袋1 小时前
Jenkins知识梳理(2)
运维·jenkins
yumgpkpm1 小时前
Acceldata ODP 3.3.6.4 vs CDP Private Cloud Base 7.3.2 对比
大数据·运维·服务器·hadoop·华为·zookeeper·hbase
Madison-No71 小时前
多语言聊天大模型--测试报告
linux·git·python·selenium·jmeter·自动化·postman
Liuqy-052 小时前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器
流浪0012 小时前
Linux系统篇47——线程(十二) POSIX 信号量与环形队列,把判空判满提前到访问之前
linux·操作系统·线程·信号量·环形队列
咖丨喱2 小时前
【MMC Core + hc16 Host(WiFi SDIO 如何落到控制器)】
linux
等我调个Bug2 小时前
【Linux系统篇】权限
linux·服务器
考研保研资料分享2 小时前
自动化控制保研经验合辑:上交夏令营、北工大预推免与跨专业面试复盘(2025)
运维·面试·自动化
乐维_lwops3 小时前
2026选择运维监控系统时应该重点考察哪些功能?
大数据·运维·人工智能