这是在帮一个用户看服务器遇到的问题,他用的是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 的关系,这类问题将不再神秘。