因为文件被进程占用时删除仅解除链接,内核保留数据块;lsof显示(deleted)说明fd仍打开,空间未释放,需重启服务或安全终止进程。

为什么 lsof | grep deleted 找到的文件删不掉还占空间
因为 Linux 文件系统里,“删除”只是解除文件名到 inode 的链接,如果进程仍在打开该文件(lsof 能看到),内核会保留其数据块,磁盘空间不会释放。常见于日志轮转没重启服务、程序未关闭 fd 就 unlink 了文件。
这类文件在 lsof 输出中表现为路径带 (deleted),但 ls 查不到,du 也统计不到——它们只活在进程的 file descriptor 里。
- 典型输出:
nginx 1234 www-data 10u REG 253,1 128765432 123456 /var/log/nginx/access.log (deleted) - 注意
10u是 fd 编号,REG表示普通文件,数字128765432是字节数(可直接换算成 MB) - 不能用
rm删除,也不能清空/proc/PID/fd/10(会报Text file busy或权限拒绝)
如何安全释放这些已删除但仍被占用的空间
核心思路:让持有该 fd 的进程主动关闭它,或终止进程。不能暴力 echo > /proc/PID/fd/N,那会破坏进程行为甚至导致崩溃。
- 优先尝试优雅重启服务:
systemctl reload nginx或kill -USR1 $(pgrep nginx)(很多服务支持 USR1 重载日志) - 若必须终止进程,用
kill -15 PID(SIGTERM),等几秒再kill -9 PID(仅当 SIGTERM 无效时) - 确认释放:执行后再次运行
lsof | grep deleted,对应行应消失;同时df显示可用空间回升 - 不要尝试
cp /dev/null > /proc/PID/fd/N—— 大部分情况会失败,且可能触发进程异常写入
一键定位并提示大文件的检查脚本
下面这个 shell 片段能快速列出按大小排序的 top 10 占用型 deleted 文件,并给出对应 kill/reload 建议:
lsof -n | awk '$5 == "REG" && $9 ~ /\(deleted\)$/ {gsub(/\(deleted\)$/, "", $9); print $7, $2, $1, $9}' | sort -nr | head -10 | while read size pid comm path; do
echo "$(numfmt --to=iec-i --suffix=B $size) | PID $pid | $comm | $path"
case "$comm" in
nginx|httpd|apache*) echo " → Suggest: systemctl reload $comm or kill -USR1 $pid";;
java) echo " → Suggest: check logback/log4j config, or restart app";;
node|python|php-fpm) echo " → Suggest: restart service, fd likely leaked";;
*) echo " → Suggest: kill -15 $pid (then verify)";;
esac
done
注意:numfmt 在较新 GNU coreutils 中才有,如无可用 awk '{printf "%.1f MiB\n", $1/1024/1024}' 替代。
最容易被忽略的关键点
不是所有 (deleted) 都值得处理——有些是短命进程临时打开又 unlink 的文件,几秒后自动消失;盲目 kill 可能中断关键任务。
- 先看
PID对应进程是否还在提供服务(ps -p PID -o pid,comm,args) - 关注
$7(文件大小)是否真影响磁盘水位(比如 > 100MB 且df /已 -
/proc/PID/fd/下的符号链接指向anon_inode:[deleted]是正常内核对象,不算磁盘占用,别和真正的大文件混淆 - 容器环境要进对应 namespace 查(
nsenter -t PID -m -u -i -n -p bash),宿主机lsof可能看不到容器内 fd










