Linux删除大文件后磁盘空间没释放:lsof、进程句柄与Journal清理检查清单
先用 df -hT 确定真正满的文件系统,再在同一挂载点比较 du -x。运行 lsof +L1 查找链接数小于 1 的已删除打开文件,重点查看进程、PID、FD、文件大小和路径。优先让对应服务正常重新打开日志,例如使用受支持的 reload、日志轮转或有序重启;不要直接操作 /proc/PID/fd 截断生产文件,除非已经确认写入语义、备份与回滚。若占用来自 systemd journal,先查看 journalctl --disk-usage,必要时 rotate 后清理归档日志,并配置长期上限。
直接答案
先用 df -hT 确定真正满的文件系统,再在同一挂载点比较 du -x。运行 lsof +L1 查找链接数小于 1 的已删除打开文件,重点查看进程、PID、FD、文件大小和路径。优先让对应服务正常重新打开日志,例如使用受支持的 reload、日志轮转或有序重启;不要直接操作 /proc/PID/fd 截断生产文件,除非已经确认写入语义、备份与回滚。若占用来自 systemd journal,先查看 journalctl --disk-usage,必要时 rotate 后清理归档日志,并配置长期上限。
一、先确认哪个文件系统已满
df 统计文件系统块使用情况,du 统计从目录树仍可遍历到的文件。容器 overlay、独立 /var、挂载卷和 bind mount 可能让路径看起来相同,却属于不同文件系统。先记录设备、类型、挂载点、总量、已用和可用空间。
使用 du -x 限制在目标文件系统内,避免跨入其他挂载点。不要一开始就在根目录执行高并发全盘扫描,生产主机上应限制范围和 I/O 优先级。
二、为什么rm后空间还在
rm 删除的是目录中的名字,本质上调用 unlink。若进程此前打开了该文件,内核中的打开文件描述仍然有效,进程可以继续写入。此时 du 无法通过原路径看到文件,但 df 仍统计其数据块。
这常发生在应用、Nginx、数据库或日志代理持续写日志,而管理员直接删除当前日志文件。正确日志轮转应通知进程重新打开文件,或使用应用支持的轮转机制。
三、使用lsof +L1定位
lsof 手册说明,+L1 选择仍被打开但链接计数小于 1 的文件。常用检查为:
sudo lsof +L1
输出中关注 COMMAND、PID、USER、FD、SIZE/OFF 和 NAME。若系统文件很多,可按目标文件系统、进程或路径进一步过滤。非 root 用户可能看不到其他用户进程的全部文件,因此“没有输出”不一定证明不存在。
四、安全释放空间
最稳妥的方法是让持有文件的进程关闭旧描述符。先确认服务名称、流量、可用副本和重启影响;如果应用支持重新打开日志的信号或 reload,应使用官方方式。否则在负载均衡中逐实例摘流、重启并验证。
不要直接 kill -9 关键进程。强杀可能中断事务、损坏临时状态或触发同时重启。也不要盲目向 /proc/PID/fd/N 写空内容,进程的当前偏移、追加模式和日志库行为可能造成稀疏文件或后续异常。
五、验证空间真正回收
服务关闭旧句柄后,重新运行 lsof +L1,确认目标记录消失,再检查 df -hT。同时观察服务状态、错误日志、监听端口和关键请求。
若空间没有变化,检查是否还有其他 PID、容器中的进程或同一文件系统上的删除文件。容器宿主机和容器命名空间可能看到不同进程视图,需要在正确环境中诊断。
六、systemd Journal占用
使用 journalctl --disk-usage 查看 active 与 archived journal 文件的总占用。官方文档说明,--vacuum-size、--vacuum-time 和 --vacuum-files 只清理归档文件,活动日志不会被直接 vacuum。
需要立即压缩历史时,可评估先 rotate 再 vacuum,但要遵守日志保留、审计和故障取证要求。清理前保存必要事件,不要在事故调查中无备份删除唯一证据。
七、配置长期Journal上限
在 journald.conf 中可使用 SystemMaxUse、SystemKeepFree、RuntimeMaxUse 等控制持久或运行时日志空间。配置后验证实际存储位置是 /var/log/journal 还是 /run/log/journal,并记录变更与回滚。
上限不是替代日志治理。持续日志风暴应修复产生源、重复堆栈和错误重试,否则清理只会暂时恢复空间。
八、du与df差异的其他原因
已删除打开文件不是唯一原因。还要检查文件系统保留块、inode 耗尽、快照、稀疏文件、硬链接、容器层和文件系统元数据。df -i 可检查 inode;大量小文件可能在容量尚有剩余时先耗尽 inode。
硬链接文件只有所有目录链接都删除后链接计数才归零。快照和存储系统的回收规则也可能让宿主机工具看到不同结果,应使用对应平台工具验证。
九、日志轮转为什么会失败
常见错误是轮转脚本把当前文件 rename 或删除,却没有通知应用重新打开;应用继续写旧 inode,新文件存在但没有内容。另一个风险是 copytruncate 在复制与截断之间可能丢失少量日志,并不适合所有高价值审计场景。
优先使用应用原生滚动或 rotate 后明确 reopen。验证应包括旧句柄关闭、新文件持续增长、权限与 owner 正确,以及日志采集器跟随了新文件。
十、生产处置顺序
1. 保存 df、du、inode 和挂载信息。
2. 找出 lsof +L1 中最大记录。
3. 将 PID 映射到服务、容器和负责人。
4. 确认备份、流量切换与重启影响。
5. 正常 reload 或逐实例重启。
6. 验证描述符关闭、空间回收和服务健康。
7. 检查 journal、快照和其他差异来源。
8. 修复日志轮转和容量告警。
十一、常见错误
- 看到磁盘满就递归删除未知目录。
- 删除当前日志但不让进程重新打开。
- 只看du,不看df与挂载点。
- 直接kill关键数据库进程。
- 在事故取证前清空journal。
- 把vacuum当作永久解决日志风暴。
- 忽略容器命名空间与宿主机差异。
- 没有验证新日志文件继续写入。
十二、FAQ
重启服务器一定会释放吗?
通常会关闭所有进程描述符,但代价很高,也可能掩盖根因。优先识别单个服务并有序处理。
lsof没有结果为什么df仍然满?
可能权限不足、检查了错误命名空间,或根因是快照、保留块、inode、硬链接和文件系统元数据,而不是删除文件。
可以直接截断/proc/PID/fd中的文件吗?
风险较高。可能影响偏移和写入语义;除非已确认应用行为并具备回滚,否则应让进程正常关闭或重新打开。
journalctl vacuum后为什么占用仍然较大?
vacuum主要删除归档journal,活动文件仍被统计。可结合rotate,并检查长期上限和日志产生速率。
十三、总结
删除文件后空间不释放,本质上是“路径已消失,但数据仍被打开引用”。先用挂载点、df与du确认范围,再用 lsof +L1 找到持有者,通过正常 reopen、reload 或逐实例重启关闭句柄。Journal、inode、快照和容器层要单独验证,最后修复轮转和容量策略,避免同类故障复发。
官方参考资料
- Linux man-pages:lsof(8),https://man7.org/linux/man-pages/man8/lsof.8.html(核验日期:2026-08-29)
- systemd:journalctl,https://www.freedesktop.org/software/systemd/man/latest/journalctl.html(核验日期:2026-08-29)
- systemd:journald.conf,https://www.freedesktop.org/software/systemd/man/latest/journald.conf.html(核验日期:2026-08-29)