朕喜技术(ZhenxiJS)
2026.08.31
1 分钟阅读
磁盘"满了"却查不出谁占:deleted file 让 df 和 du 对不上
问题背景
运维接到告警:数据盘 df -h 显示 已用 100%,写文件报 No space left on device。但:
du -sh /data/*全盘加起来很小,找不到那个大文件;- 或删除了大日志
rm -f /data/app/*.log后df依旧 100%,空间一点不释放。
磁盘空间"神秘消失"了。这是生产环境非常常见的坑,根因是已删除但仍被进程打开的文件。
复现步骤
需要 Docker。在 scenario/2026-08-31-0800/ 下:
docker compose up -d --build
docker compose exec app bash
python3 /workspace/scenario/2026-08-31-0800/hold_deleted.py &
sleep 25
df -h /data # 使用率上涨(本例 tmpfs 300M 用到 200M/67%)
du -sh /data/* # 目录里却查不到任何文件
ls -la /data/ # 找不到那个大日志
根本原因
进程 open() 一个大文件持续写入,之后文件被 unlink(logrotate / 误删),但进程没有 close 文件句柄。于是:
- 目录里已找不到该文件 →
du统计不到; - 但内核仍认为文件被占用,inode 数据块一直占着磁盘 →
df仍显示已用; - 只有进程关闭句柄 / 退出,空间才会释放。
解决办法
定位:用 lsof +L1 列出"已删除但仍被打开"的文件:
lsof +L1 | grep deleted
# python3 14 root 3w REG 0,145 209715200 0 ... /data/huge_app_data.log (deleted)
ls -l /proc/14/fd/ | grep deleted # 3 -> /data/huge_app_data.log (deleted)
释放:不需重启整机,让持有 deleted 句柄的进程退出(或关闭句柄):
kill 14 # SIGTERM 优雅终止
sleep 2
lsof +L1 | grep deleted # (无输出)
df -h /data # 使用率回落为 0
预防:
- logrotate 配置加
copytruncate,先 copy 再 truncate,句柄指向同一 inode,日志不会真正 unlink。 - 写入进程要正确
close()文件句柄。 - 例行巡检用
lsof +L1排查 deleted 占用大的进程,及时优雅重启。 df -h与du -sh对账发现不一致时,优先怀疑 deleted-but-held 文件。
验证
df -h /data使用率显著下降(本例 67% → 0%);lsof +L1 | grep deleted不再有占用大空间的 deleted 文件;- 可正常写入新文件,不再报
No space left on device。
经验与避坑
du查不到不代表磁盘没占:deleted-but-held 文件是"隐形占用",必须lsof +L1才能看到。- 清理前先对账:
df与du相差明显时,先lsof +L1,别盲目删文件。 kill优先用优雅信号(SIGTERM),让进程自行清理;必要时 SIGKILL。- 生产环境:日志轮转务必配
copytruncate,否则迟早遇到"删了日志空间不释放"。
技术栈关键词
Linux 运维、磁盘空间不足、df、du、lsof、deleted file、lsof +L1、No space left on device、日志轮转、logrotate copytruncate、inode、已删除但被进程占用的文件、/proc/
