遇到磁盘空间“明明删了文件却没释放”的情况,直接用lsof +L1往往就能把问题揪出来:输出里NAME列带“(deleted)”的,就是已经删除但仍被进程占着的文件;SIZE/OFF会显示它实际占用了多少字节,PID和COMMAND则对应具体是哪个进程在持有。要是权限不够,或者这条命令没有查到结果,那就继续往底层看,用find /proc/*/fd -ls 2>/dev/null | grep deleted来进一步定位。

怎么用 lsof 找出已删除但仍在占用空间的文件
当 df -h 显示磁盘已满,而 du -sh /* 统计总和远小于已用空间时,大概率是文件被 rm 删除了,但进程还开着它的文件描述符——数据块没释放,空间就“悬着”。这种文件在 ls 或 du 下完全不可见,只能靠 lsof 挖出来。
执行以下命令即可列出所有这类“幽灵文件”:
lsof +L1
输出中会显示类似这样的行:
COMMAND PIDUSER FD TYPE DEVICE SIZE/OFFNODE NAME ja va 1234app 12w REG253,0 2845678901 1234567 /var/log/app/nohup.out (deleted)
关键点:
(deleted)出现在NAME列末尾,就是确认标志SIZE/OFF列数值越大,说明它占的空间越多(单位是字节)PID和COMMAND告诉你哪个进程在 hold 它
lsof +L1 没输出但空间仍对不上,怎么办
有些系统(比如容器化环境或低权限用户)下 lsof +L1 可能因权限不足漏掉结果。这时换用更底层、权限要求更低的方式:
find /proc/*/fd -ls 2>/dev/null | grep deleted
这条命令直接扫描所有进程的文件描述符目录,只要内核还维护着该句柄,就能捕获。输出格式更原始,但更可靠。常见现象包括:
- 大量
ja va或nginx进程的access.log (deleted) dockerd持有已删的容器日志或临时层文件- 长时间运行的脚本(如
nohup ./run.sh &)把 stdout 重定向到一个后来被删的文件
为什么不能只靠 du 和 df 对比就下结论
du 统计的是当前文件系统目录树里“可见路径”下的实际块占用;df 统计的是整个文件系统级的已分配块总数。两者差值 ≠ 一定是“已删未释放”——还有几个干扰项必须先排除:
- ext4 默认为 root 预留 5% 空间:
tune2fs -l /dev/vda1 | grep "Reserved block count"可查,这部分不显示在du里,但算在df的 Used 中 - 挂载覆盖:比如
/mnt/data原本有内容,后来挂了新磁盘上去,旧文件就被遮住,du扫不到,但空间仍被占 - tmpfs 或 overlayfs 等内存/虚拟文件系统,不走真实磁盘块分配逻辑
所以,务必先运行 df -i 看 inode 是否耗尽,再用 tune2fs -l 确认预留比例,最后才聚焦到 lsof +L1 ——否则容易误判。
释放这类空间,重启进程是最稳妥的做法吗
不一定。重启虽然一劳永逸,但在生产环境往往不可行。更轻量的替代方案包括:
- 让进程自己 reload 日志(如
kill -USR1 $(pgrep nginx)),多数服务会关闭并重建日志 fd - 手动清空文件内容而不删句柄:
cp /dev/null /proc/1234/fd/12(需 root,且仅适用于可写 fd) - 如果确认是某个日志文件,且应用支持 logrotate,直接运行
logrotate -f /etc/logrotate.d/myapp
注意:echo "" > /proc/PID/fd/N 在部分内核版本或文件系统上可能失败,cp /dev/null 更稳定。但任何操作前,请确认该 fd 对应的服务行为——盲目截断可能造成日志错乱或程序异常。