朕喜技术(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

预防:

  1. logrotate 配置加 copytruncate,先 copy 再 truncate,句柄指向同一 inode,日志不会真正 unlink。
  2. 写入进程要正确 close() 文件句柄。
  3. 例行巡检用 lsof +L1 排查 deleted 占用大的进程,及时优雅重启。
  4. 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//fd

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-08-31-0819-dfdu-deleted-file-lsof

分享这篇文章