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 的关系,这类问题将不再神秘。

相关推荐
92year3 小时前
用Google ADK从零搭一个能调工具的AI Agent:Python实操全过程
python·ai·mcp
woxihuan1234564 小时前
SQL删除数据时存在依赖关系_设置外键级联删除ON DELETE
jvm·数据库·python
Jetev4 小时前
如何确定SQL字段是否为空_使用IS NULL与IS NOT NULL
jvm·数据库·python
蛐蛐蛐5 小时前
昇腾910B4上安装新版本CANN的正确流程
人工智能·python·昇腾
m0_702036535 小时前
mysql如何处理不走索引的OR查询_使用UNION ALL优化重写
jvm·数据库·python
2401_846339565 小时前
MySQL在云环境如何选择存储类型_SSD与高性能云盘配置建议
jvm·数据库·python
2601_957780846 小时前
Claude 4.6 对阵 GPT-5.4:2026 开发者大模型 API 选型深度解析
人工智能·python·gpt·ai·claude
2601_957780846 小时前
GPT-5.5 深度解析:2026年4月OpenAI旗舰模型的技术跨越与商业决策指南
大数据·人工智能·python·gpt·openai
zhaoyong2226 小时前
SQL如何统计每个用户的首次行为时间_MIN聚合与分组
jvm·数据库·python
2501_901006476 小时前
C#怎么实现配置热更新 C#如何在运行时动态刷新配置文件不需要重启程序【技巧】
jvm·数据库·python