返回首页

df du 差异:查找 Linux 中已删除文件

在 Linux 中 df 和 du 差异由于进程打开描述符持有的已删除文件。本文解释 inode 机制,通过 lsof +L1 搜索以及 > /proc/PID/fd/FD 无需重启释放空间。对 logrotate copytruncate 和监控的推荐。

df 说谎?不,在 lsof 中搜索已删除文件
Advertisement 728x90

df与du为何不一致:在Linux上查找隐藏的千兆字节

df -h命令显示磁盘使用率为95%,而du -sh /*仅汇总出20%。剩余的75%对du不可见,却占用了磁盘块。原因是进程持有的打开文件描述符指向已删除的文件。df直接读取文件系统超级块,du遍历目录树并忽略其中没有条目的文件。

inode存储文件元数据,包括数据块列表。rm仅移除目录条目(硬链接),inode链接计数下降,但如果进程持有描述符,块仍保持分配状态。

图示:

Google AdInline article slot
删除前:
目录        inode          数据块
/var/log/  --> app.log  -->  [块1][块2]
               ^
               nginx描述符 (pid 1234, fd 7)

删除后:
目录        inode          数据块
/var/log/  --> (已删除) -->  [块1][块2]
               ^
               nginx描述符

du在/var/log/中看不到该文件,df记录占用的块。

问题的典型场景

日志轮转:logrotate对旧文件执行rm,但nginx或java持有启动时的描述符。进程写入已删除的文件,df记录使用量增加,du则不会。

其他场景:

Google AdInline article slot
  • 长期运行的守护进程打开日志文件。
  • 清理脚本中的错误,其中rm先于描述符关闭。
  • Docker容器中内部进程持有主机文件。

问题会累积:千兆字节在不知不觉中消失,直到磁盘填满。

查找持有已删除文件的进程

使用lsof +L1查找NLINK < 1的文件(从文件系统删除但打开):

COMMAND  PID  USER  FD  TYPE  DEVICE  SIZE/OFF  NLINK  NODE  NAME
nginx   1234  www   7w  REG   8,1  2147483648  0  12345  /var/log/app.log (deleted)
java    5678  app   3w  REG   8,1   536870912  0  67890  /var/log/service.log (deleted)

关键字段:

Google AdInline article slot
  • COMMAND/PID:进程和ID。
  • FD:描述符(7w — 写入,编号7)。
  • SIZE/OFF:大小 — 丢失GB的来源。
  • NLINK=0:删除确认。
  • NAME (deleted):rm前的路径。

大小过滤:lsof +L1 | awk '$7 > 104857600 {print}'(文件>100MB)。

无需重启释放空间

对于进程PID 1234,FD 7:

$ > /proc/1234/fd/7

/proc是内存中的procfs。该命令通过描述符将文件清零,内核立即释放块。df更新,进程继续写入fd 7。

检查:lsof +L1 | grep 1234 — SIZE/OFF=0或为空。

脚本自动化:

#!/bin/bash
lsof +L1 | awk '$7 > 104857600 && $9 ~ /deleted/ {print $2, $8}' | while read pid fd; do
  echo "清零 $pid/$fd"
  > /proc/$pid/fd/$fd
  echo "已释放"
done

在logrotate中预防问题

避免在轮转中使用rm。在/etc/logrotate.d/app中:

/var/log/app.log {
    daily
    rotate 7
    compress
    copytruncate
}

copytruncate复制日志,然后截断原始文件(>机制)。描述符不变。

缺点:复制时的微延迟 — 可能丢失行。

零丢失替代方案:

  • logrotate:mv旧文件 → 新文件。
  • kill -HUP <pid>(SIGHUP用于重新打开日志)。

nginx:nginx -s reopen。systemd:systemctl reload nginx

监控与告警

检查脚本:

lsof +L1 -F s | awk '/^s/ && substr($0,2)+0 > 104857600 {count++} END {print count+0}'

输出>100MB的已删除文件计数。

Prometheus集成:

  • node_filefd_allocated — 打开fd的增长。
  • 比较df/disk_used与du总和。

每5分钟cron:如果差异>10%则告警。

关键要点

  • df显示超级块中的分配块,du显示目录中可见的文件。
  • lsof +L1通过NLINK=0查找持有(已删除)文件的进程。
  • 释放空间:> /proc/PID/fd/FD — 无需重启清零。
  • logrotate copytruncate无需HUP即可预防问题。
  • 监控:使用lsof或node_exporter指标的脚本。

— Editorial Team

Advertisement 728x90

继续阅读