CentOS 7 删除文件却不释放空间?从 inode、文件描述符到 VFS 的底层原理解析

这是在帮一个用户看服务器遇到的问题,他用的是Hostease的独立服务器,因为服务器用的时间也比较长了,磁盘比较满,所以做了一次清理,但是在删除文件之后,发现他删除掉几百个G的数据之后,磁盘空间并没有空出来。

我拿到之后先df -h看了下:

光看df结果,文件确实已经满的再满了,此时用户已经删除大量文件,空间却没有释放;并且紧接着重启了服务,而重启前后前后,df 变化也不明显。

这类问题,99% 本质都与 inode 引用计数有关。

理解 Linux 删除文件的真实过程

很多人误以为:

rm = 删除文件内容

实际上:

rm 只是删除"目录项"(directory entry)

我们必须理解 Linux 文件系统的三个核心概念:

1️.inode

inode 保存的是以下信息:

文件大小

权限

数据块指针

链接数(link count)

时间戳

文件名不存储在 inode 里。

2️.目录项(dentry)

目录本质是:

文件名 → inode 号

删除文件时,删除的是目录项,而inode和数据块仍然存在。

3️.文件描述符(File Descriptor)

当进程打开文件:

fd = open("test.log", O_WRONLY);

内核会在进程表中创建 file descriptor,引用对应 inode,增加引用计数。

什么时候磁盘空间才会真正释放?

磁盘空间释放的条件:

1.link count == 0

2.没有进程持有该 inode

只有两个条件同时满足,内核才会释放 inode和数据块,回收空间。

为什么 lsof | grep deleted 会出现大量文件?

执行:

lsof | grep deleted

示例:

php-fpm 4510 root 3u REG ... /tmp/ZCUDnRoeZv (deleted)

简单来说就是,目录项已删除,link count 已为 0,但进程仍持有 file descriptor,inode 仍被引用,数据块无法释放。

这是一种非常经典的"幽灵文件"。

从内核角度看"deleted 文件"

在 /proc 下可以看到:

ls -l /proc/4510/fd/

会看到类似:

3 -> /tmp/ZCUDnRoeZv (deleted)

实际上文件名已不存在,但 fd 仍然指向 inode,进程依然可以写入,这也是为什么日志文件可以"删掉还继续增长"。

df 与 du 为什么会不一致?

df 统计的是文件系统已分配的数据块数量,它直接读取超级块(superblock)信息。

而du 是遍历目录树,统计可见文件的 inode 占用。

deleted 文件会导致什么?

df 统计的是实际已用 block

du 统计的是当前目录可见文件

因此:

df 显示 100%

du 却找不到大文件

这几乎可以确定:

存在被进程占用的 deleted 文件

真实排查流程(技术向)

1.查看磁盘使用

df -h

2.查看目录占用

du -xhd1 / 2>/dev/null | sort -hr

如果 du 总和远小于 df,说明存在"不可见占用"。

3️.检查 deleted inode

lsof | grep deleted

或:

lsof -nP | grep '(deleted)'

4️.查看具体进程 fd

ls -l /proc/PID/fd

5. 释放方式

推荐方式

优雅重启服务:

systemctl restart 服务名

非优雅方式(极端场景)

直接 kill 进程:

kill -9 PID

不建议生产环境滥用。

隐藏回收站目录的本质

例如:

/.Recycle_bin

这不是内核机制,而是:

面板逻辑层实现

文件被移动而非删除

底层发生的是:

rename(old_path, /.Recycle_bin/xxx)

inode 不变,仅目录项变化。

因此:

du 会看到

df 会统计

但用户误以为已删除

在这个客户的案例里,虽然存在一些幽灵文件,但实际上大头是在/.Recycle_bin 中:

ext4 延迟分配与日志机制补充

在 ext4 下:

采用延迟分配(delayed allocation)

使用 journal 日志机制

如果磁盘 100%:

journal 无法提交

某些写入可能阻塞

服务异常

因此:

根分区长期 100% 是非常危险的状态。

进阶:如何快速统计 deleted 占用总量?

可以使用:

lsof | grep deleted | awk '{print 7}' \| awk '{sum+=1} END {print sum/1024/1024 " MB"}'

用于估算被占用空间。

总结

删除文件 ≠ 释放空间,真正释放空间的条件是:

inode link count = 0

且无 file descriptor 引用

df 与 du 不一致的根本原因是df 统计 block,du 统计目录可见 inode,当你理解了 inode 与 file descriptor 的关系,这类问题将不再神秘。

相关推荐
dev派1 分钟前
AI Agent 系统中的常用 Workflow 模式(2) Evaluator-Optimizer模式
python·langchain
前端付豪2 小时前
AI 数学辅导老师项目构想和初始化
前端·后端·python
用户0332126663672 小时前
将 PDF 文档转换为图片【Python 教程】
python
悟空爬虫3 小时前
UV实战教程,我啥要从Anaconda切换到uv来管理包?
python
dev派4 小时前
AI Agent 系统中的常用 Workflow 模式(1)
python·langchain
明月_清风6 小时前
从“能用”到“专业”:构建生产级装饰器与三层逻辑拆解
后端·python
曲幽15 小时前
数据库实战:FastAPI + SQLAlchemy 2.0 + Alembic 从零搭建,踩坑实录
python·fastapi·web·sqlalchemy·db·asyncio·alembic
用户83562907805120 小时前
Python 实现 PowerPoint 形状动画设置
后端·python
ponponon21 小时前
时代的眼泪,nameko 和 eventlet 停止维护后的项目自救,升级和替代之路
python
Flittly21 小时前
【从零手写 ClaudeCode:learn-claude-code 项目实战笔记】(5)Skills (技能加载)
python·agent