df -h 已经 100%,于是你 rm 删了一个几十 G 的日志,再一看——还是 100%。先给结论:
文件被运行中的进程打开着。
rm只是把文件从目录里"摘掉"(解除链接),但只要还有进程持有它的文件句柄,占用的磁盘块就不会释放。要找到是谁,用lsof +L1。
很多日志/程序(tail、java、数据库、nginx、某些写日志的后台服务)都会持续持有文件句柄,删了文件也一样占着空间,直到进程退出或服务重启。
第 1 步:确认空间确实没释放
df -h /
如果删文件前后都接近 100%,基本可以判断是被占用未释放。
第 2 步:找出被删除但仍被占用的文件
核心命令,列出所有 link count 为 0(已被删除)却仍被打开的文件:
lsof +L1
输出里 COMMAND 是进程名,PID 是进程号,NAME 会显示类似 (deleted) 的删除标记:
lsof +L1 2>/dev/null | grep -i deleted
# 只看有没有删除文件;通常长这样:
# COMMAND PID USER FD TYPE DEVICE ... NAME
# java 12345 root 1w REG 8,1 ... /var/log/app.log (deleted)
提示:
lsof若未安装,Debian/Ubuntu 用sudo apt install lsof,RHEL/CentOS/Rocky 用sudo yum install -y lsof。
第 3 步:确认是哪个进程、能否处理
# 用 PID 看进程详情
ps -fp <PID>
# 只想看某个目录下的删除占用
lsof +L1 2>/dev/null | grep '/var/log'
第 4 步:释放空间
最干净的方式是让持有句柄的服务正常重启,句柄关闭后块即归还:
# 以 nginx / systemd 服务为例
sudo systemctl restart nginx
# 确认空间释放
df -h /
如果你不确定是哪个服务,先 lsof +L1 看 COMMAND 与路径,再针对性重启对应服务(如 java 进程一般是应用,nginx/httpd 是 Web 服务)。
谨慎:不要直接 kill 关键生产进程。能优雅重启就优雅重启;确需 kill 时先确认该进程可被拉起。
如果一直查不到"删除文件"的进程
空间仍满但 lsof +L1 为空,可能是:
- df 与 du 口径问题——见同系列《df -h 和 du 不一致》;
- inode 耗尽导致看似"没空间"——见《inode 耗尽》;
- 文件在别的挂载点/快照里,用
df -h确认到底是哪个分区满。
一个更彻底的小技巧:清空而非删除日志
对于正在被写的日志,推荐用"清空文件内容"代替"删除文件",这样不需要重启进程也能立刻释放空间,且进程写入不中断:
# 用 truncate 或直接 > 清空,而非 rm
sudo truncate -s 0 /var/log/bigapp.log
# 或 sudo sh -c '> /var/log/bigapp.log'
df -h /
提示:清空要用重定向把内容置 0 而不是
rm,才能即时释放且不影响持有句柄的进程。
FAQ
问:rm 删了大文件空间立刻就该释放吗? 只有当没有进程持有该文件句柄时才立刻释放;被进程占用的文件要等进程退出/重启。
问:怎么快速找到"删除文件但没释放"的进程?
lsof +L1 | grep deleted,看 COMMAND 和 PID。
问:不能重启服务怎么释放空间?
用 > 文件 或 truncate -s 0 文件 把文件清空(针对正在写的日志),或视情况处理占用进程。
问:为什么 tail -f 也会导致空间不释放?
tail -f 会持续打开文件句柄;若先 rm 再 tail 某文件的旧路径,句柄仍指向被删文件,空间就占着。
想系统看懂磁盘占用,可看同系列:df/du 不一致的原因、inode 耗尽、磁盘清理实战。