Linux 删除文件后磁盘空间未释放的排查

13次阅读

Linux 中文件被 rm 删除后若进程仍持有句柄,磁盘空间不会释放,因 inode 和数据块仍被占用;需用 lsof +L1 排查,通过 kill -USR1 或重启进程释放 fd,而非再次 rm。

Linux 删除文件后磁盘空间未释放的排查

进程仍持有已删除文件的句柄

Linux 中文件被 rm 删除后,若仍有进程打开该文件(即持有其文件描述符),磁盘空间不会立即释放——内核仅将目录项移除,但 inode 和数据块仍被占用,直到所有引用该文件的 fd 关闭。

常见于日志类服务(如 nginxjava 应用、rsyslog)持续写入被删文件,或运维误删正在滚动的日志。

排查方法:

  • lsof +L1 列出所有“已删除但仍被打开”的文件(+L1 表示 link count 为 0)
  • 或用 lsof | grep deleted 筛选含 deleted 字样的行
  • 关注输出中的 PIDCOMMANDFD(如 12w 表示写入模式的第 12 号 fd)和文件路径(末尾带 (deleted)

确认空间是否真未释放

别只看 df,它反映的是文件系统级使用量;而 du -sh /path 统计的是目录下可见文件大小之和。二者差异大时,大概率是“已删文件仍被进程占用”。

执行以下命令交叉验证:

  • df -h / 查看挂载点整体使用率
  • du -sh /* 2>/dev/null | sort -hr | head -5 快速定位大目录(忽略权限错误)
  • du -sh /proc/*/fd/* 2>/dev/null | grep 'deleted' | head -5 直接搜进程 fd 下的 deleted 文件(需 root 权限)

注意:du 不会统计被删但未关闭的文件,所以 du 总和远小于 df 已用空间,就是典型信号。

释放空间的正确操作

不能靠再次 rm 或重启机器来“解决”——必须让持有句柄的进程释放 fd。方式取决于进程是否支持热重载:

  • 对支持 kill -USR1 重开日志的程序(如 nginxhttpd),执行 kill -USR1 ,它会关闭旧日志 fd 并新建
  • systemd 管理的服务,优先用 systemctl kill --signal=USR1
  • 若进程不支持重载,且业务允许中断,可 kill -HUP (部分服务接受 HUP 重开日志)或直接 systemctl restart
  • 极端情况(如僵尸进程或无法重启的服务),可用 echo > /proc//fd/ 清空 fd 内容(仅限可写文件,且不推荐生产环境盲操作)

操作后,再运行 lsof +L1 应无输出,df 显示的空间也会立刻回落。

预防与监控建议

这类问题往往在磁盘告警后才暴露,但根源在于运维习惯和日志管理策略。

关键点:

  • 禁用直接 rm *.log 清日志,改用 logrotate 配置 rotate + copytruncate 或发送 SIGUSR1
  • 定期检查 lsof +L1(可加入巡检脚本),尤其在批量清理日志后
  • 对 Java 应用,确认 logbacklog4j2 的 rolling policy 是否配置了 deleteOnRollover 或依赖外部 rotate
  • /proc/sys/fs/inotify/max_user_watches 过低也可能导致某些日志轮转失败,间接引发堆积

真正棘手的不是找不到谁占着空间,而是某个老版本中间件静默持有 fd 十几天,期间没人动过它——这种 case 一定要查 lsof +L1,而不是反复 du

text=ZqhQzanResources