No space left on device,不影响宿主机根分区。df、du、lsof +L1 区分“空间真被文件占用”和“文件已删除但仍被进程持有”。准备一台 Linux 虚拟机或一次性特权容器,具备 root、mkfs.ext4、mount 和 losetup,/tmp 至少留 200 MB。所有写入只发生在 /tmp/lab-disk 和 /tmp/lab-mnt,不要对 /、/var、/home 做填充实验。
sudo -i
mkdir -p /tmp/lab-disk /tmp/lab-mnt
truncate -s 128M /tmp/lab-disk/disk.img
mkfs.ext4 -q -F /tmp/lab-disk/disk.img
mount -o loop /tmp/lab-disk/disk.img /tmp/lab-mnt
findmnt -T /tmp/lab-mnt
df -h /tmp/lab-mnt
确认 findmnt 的挂载点是 /tmp/lab-mnt、源设备是 loop 设备后再继续。若机器不支持 loop 挂载,可改用 mount -t tmpfs -o size=128m tmpfs /tmp/lab-mnt,其余步骤不变。
先写入一个接近容量的文件,再追加数据触发 ENOSPC:
dd if=/dev/zero of=/tmp/lab-mnt/fill.bin bs=1M count=120 status=progress
dd if=/dev/zero of=/tmp/lab-mnt/more.bin bs=1M count=16 status=progress
第二条命令应报 No space left on device。接着制造“已删除但仍被打开”的文件,模拟日志进程占着空间:
bash -c 'exec 3<>/tmp/lab-mnt/hold.log; rm -f /tmp/lab-mnt/hold.log; sleep 600' &
echo $! > /tmp/lab-disk/holder.pid
df -h /tmp/lab-mnt
df -i /tmp/lab-mnt
du -x -h --max-depth=1 /tmp/lab-mnt | sort -h
lsof +L1 | grep lab-mnt
预期结果是:df -h 使用率接近 100%,普通写入失败;df -i 仍有富余,说明不是 inode 耗尽;du 统计明显小于 df 的已用量,差额来自已删除但仍被打开的 hold.log;lsof +L1 能看到该文件的 (deleted) 记录。
df -h 的根分区。df -h 满而 df -i 正常,是空间耗尽;反过来则是 inode 耗尽。du 与 df 差异大时,优先检查 lsof +L1、文件系统保留块和快照。df 显示 100% 不代表所有进程都不能写。ext4 默认给 root 保留约 5%,root 仍可能写入,普通用户已经失败。也不能因为 du 统计偏小就认为空间“凭空消失”。kill -9 数据库进程。rm -f /tmp/lab-mnt/fill.bin
kill "$(cat /tmp/lab-disk/holder.pid)"
df -h /tmp/lab-mnt
dd if=/dev/zero of=/tmp/lab-mnt/verify.bin bs=1M count=1 status=none
写入 verify.bin 成功即表示恢复。生产环境还应检查日志轮转、core dump、镜像层和备份目录,并补充容量告警。
kill "$(cat /tmp/lab-disk/holder.pid)" 2>/dev/null || true
umount /tmp/lab-mnt
rm -f /tmp/lab-disk/disk.img /tmp/lab-disk/holder.pid
rmdir /tmp/lab-mnt /tmp/lab-disk
umount 失败时先用 lsof +f -- /tmp/lab-mnt 找到占用者,不要直接对挂载点执行 rm -rf。
du 与 df 不一致时应立即想到它。df -h 显示 100%,但 du 统计只有一半,可能是什么原因?du 没有覆盖到对应挂载点。lsof +L1 找到持有已删除文件的进程,重载或重启它,而不是继续盲目删除。