Linux 文件删了,df 为什么不降?用 /proc 和 lsof 找到占空间的 FD
执行 rm 后,目录里看不到大文件,df -h 的使用量却没变化,常见原因是:文件名已经解除链接,但某个进程仍持有该文件的打开文件描述符(FD)。目录项负责让路径找到文件;进程的 FD 则是内核对打开对象的引用。删除最后一个目录链接,不会强行让已有 FD 失效,数据块通常要等最后一个引用关闭后才可回收。因此 ls 看不到它,df 仍会计入块占用。
先在临时目录复现
下面的 Python 例子用于 Linux、macOS 等 Unix 系统,不适用于 Windows 中仍被打开的文件删除语义。它不碰业务目录。先创建 4 MiB 文件,解除路径,再通过仍打开的句柄读取并检查大小;关闭句柄后才清理临时目录。
import os
import tempfile
with tempfile.TemporaryDirectory(prefix="unlink-fd-") as directory:
path = os.path.join(directory, "payload.bin")
with open(path, "wb") as f:
f.write(b"x" * (4 * 1024 * 1024))
f = open(path, "rb")
os.unlink(path)
print("路径存在:", os.path.exists(path))
print("FD 对应大小:", os.fstat(f.fileno()).st_size)
print("仍可读取:", f.read(8))
f.close()
预期路径存在为 False,FD 大小为 4194304,仍能读出八个 x。本次在 macOS 上实际运行得到这些结果;macOS 没有 Linux 的 /proc,所以这里只验证 unlink 与打开 FD 的生命周期,不声称实测了 Linux 命令。Linux 可用 lsof +L1 查找链接数小于 1 的打开文件;再用 readlink /proc/<PID>/fd/<FD> 查看目标,常见结果带有 (deleted)。也可列出 /proc/<PID>/fd/ 并逐个检查。权限、容器 PID 命名空间及进程竞态可能影响可见性;lsof 无结果时,还应确认检查的是发生空间异常的主机/挂载命名空间。
确认后释放空间
先用 df -h 确认文件系统,再结合 lsof +L1 的 PID、FD、大小和命令名定位持有者;核实它确实属于可清理的日志或临时文件。优先让应用按支持的方式重开日志、完成轮转,或在维护窗口正常重启对应服务。进程关闭旧 FD 后,内核即可回收对应数据块;重新检查 df。如果仍有差异,再检查快照、其他进程引用、挂载点和文件系统统计等因素,不能仅凭这一现象认定原因。
不要看到 (deleted) 就盲目 kill:FD 可能承载数据库、队列或正在写入的日志,强杀会造成服务中断、未落盘数据丢失或恢复成本。也不要对 /proc/<PID>/fd/<FD> 直接 truncate:这会改动该打开对象的长度,会丢弃被截掉的数据,可能破坏业务文件格式;原有文件偏移不会随截断改变,后续非追加写入还可能产生空洞,并不等同于让进程关闭引用;更不应把 proc 路径当作安全的清理入口。先辨认文件用途,再通过应用生命周期释放引用。
资料
Linux man-pages 的 unlink(2)、proc_pid_fd(5)、truncate(2) 手册,lsof(8) 手册,以及 Python 标准库 os.remove / os.unlink 文档。正文的 Linux 命令是诊断步骤,未在本次 macOS 实验中运行。
- 点赞
- 收藏
- 关注作者
评论(0)