首页  /  Linux  /  正文

磁盘莫名满了?我是这样一步步找到元凶的

Linux 2026-09-28📖 8 分钟👁 —

那天报警响了:服务器磁盘使用率 100%。我登上机器,敲下最熟的两条命令,结果把自己绕进去了。

df -h          # 显示 / 已用 100%,只剩 0 字节
du -sh /*      # 加起来的占用,只有 12G ……

文件系统说满了,目录加起来却对不上。差了将近 30G。这就是运维里最经典的"幽灵磁盘"问题。

为什么会这样

Linux 里,一个文件其实分两部分:目录项(文件名 → inode)和 inode + 数据块(真正占空间的东西)。

当你 rm 一个文件时,如果还有进程正打开着它,内核只会把目录项删掉,inode 和数据块会一直保留,直到那个进程关闭文件。这时候:

du 找不到它(目录里已经没有这个名字了)
df 却算着它(空间确实还被占着)

这不是 bug,是 Unix 的设计。

三步找到它

第一步:确认是不是这个原因

lsof +L1 2>/dev/null | head -20    # 列出被删除但仍被占用的文件
COMMAND   PID USER  FD  TYPE DEVICE SIZE/OFF NLINK   NODE NAME
nginx    1421 root  12w  REG  253,3   27.4G     0 655431 /var/log/nginx/access.log (deleted)

第二步:确认是哪个进程

# 直接进 /proc 看,比 lsof 更快(不用额外装包)
for pid in /proc/[0-9]*; do
  ls -l $pid/fd 2>/dev/null | grep deleted
done
小技巧:/proc/<pid>/fd 是内核暴露的"进程打开文件表"。就算系统里没装 lsof,这条路也能走通。

第三步:释放空间

注意:不能再 rm 一次(已经没有目录项了)。得让那个进程重新打开文件:

方法命令适用场景
平滑重载systemctl reload nginx服务支持 reload(首选)
重启服务systemctl restart nginx不支持 reload 时
清空文件: > /path/to/file文件还有目录项时
强制杀进程kill -9 <pid>最后手段,可能丢数据
为什么优先 reload?reload 是平滑重载:nginx 启动新 worker 接管新请求,老 worker 处理完手头的再退出,用户无感知。restart 是先停后起,中间有服务中断。

下次怎么避免

根因其实是日志没轮转——nginx 的 access.log 一直写,从来没切过。

/var/log/nginx/*.log {
    daily           # 每天轮转
    rotate 14       # 保留 14 份
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

关键在 kill -USR1:告诉 nginx "重新打开日志文件"。没这一步,nginx 会继续往已被 rename 的旧文件写,问题照样出现。

总结

  1. df 和 du 对不上,先想到"已删除但被占用"
  2. 没有 lsof 也能查 —— /proc/<pid>/fd 是万能后门
  3. 治标更要治本 —— 配好 logrotate 才是根治

排障最有价值的,往往不是"怎么修好的",而是"为什么会这样"。