在 Linux 系统上,df 和 du 命令统计结果不一致是经常遇到的情况,其背后的原理涉及到文件系统如何管理磁盘空间和文件这两个不同的视角。
简单来说,核心差异在于:
df报告的是文件系统级别的磁盘块使用情况,它查看的是磁盘块的分配情况。du报告的是文件级别的磁盘块使用情况,它通过累加每个文件的大小来计算。
1. 命令定义与工作原理
df (Disk Free)
工作原理:
df命令直接读取文件系统的超级块(Superblock) 中的信息。超级块中存储了整个文件系统的元数据,包括总空间、已用空间、空闲空间、inode 数量等。因此,df的执行速度非常快,因为它不需要遍历所有目录和文件。它统计什么:文件系统上所有已分配的磁盘块(包括被文件占用的和部分被保留的)。
du (Disk Usage)
工作原理:
du命令会递归地遍历指定目录下的每一个文件和子目录,然后累加每个文件占用的磁盘空间大小。它的结果基于当前用户权限下能看到的文件。它统计什么:所有文件实际占用的磁盘块的总和。
2. 导致差异的常见原因分析
正是因为上述不同的工作原理,导致了以下几种常见的不一致情况:
原因一:文件被删除但未被释放(最常见!)
这是导致 df 显示空间已用而 du 找不到对应文件的最主要原因。
原理:当一个文件正在被某个进程使用时,如果它被删除(
rm),Linux 并不会立即释放其占用的磁盘空间。该进程仍然持有该文件的句柄,可以继续读写它。此时,该文件在目录中的条目(entry)消失了,所以du无法统计到它。但该文件占用的磁盘块在文件系统中仍然被标记为“已使用”,直到所有打开它的进程都关闭该文件后,空间才会被释放。表现:
df显示磁盘使用率很高,但du -sh /统计根目录占用的总空间却小很多。解决方法:
使用
lsof | grep deleted命令查找已被删除但仍被进程打开的文件及其进程ID(PID)。确认无误后,重启或终止相关进程(
kill -9 <PID>),空间便会释放。
原因二:文件系统保留空间(Reserved Blocks)
原理:默认情况下,Ext3/Ext4 等文件系统会保留约 5% 的磁盘空间(可由
tune2fs调整)给 root 用户。这样做的目的是在磁盘空间写满时,为 root 用户保留一定的操作空间(例如执行必要的恢复命令),防止系统完全崩溃。表现:
df会把这 5% 的保留空间计入“已用”空间,因为它对普通用户不可用。而du不会统计这部分空间,因为它不是被任何文件占用的。计算:对于一个 100G 的分区,
df可能会显示95G可用,5G已用(其中一部分就是保留空间),即使一个文件都没存。
原因三:挂载点与其他文件系统
原理:如果在
/data目录下挂载了另一个磁盘(例如/dev/sdb1),那么:du -sh /data会统计挂载在/data上的文件系统(/dev/sdb1)中所有文件的大小。df /data显示的是/dev/sdb1这个独立文件系统的使用情况。通常这里是一致的,但如果
/data目录本身在根分区(/)中有一些文件(在挂载前存在的),这些文件会被挂载操作“隐藏”。du看不到它们(因为访问路径被挂载点覆盖了),而df统计的是两个不同的文件系统,所以不会产生混淆。
原因四:稀疏文件(Sparse Files)
原理:稀疏文件是一种特殊文件,它允许在文件中存在“空洞”(大量连续的0字节)。应用程序(如虚拟机磁盘镜像)创建它时,文件系统不会为这些空洞分配实际的磁盘块。
表现:
ls -l或du --apparent-size显示的是文件的逻辑大小,会非常大。du(默认行为)显示的是文件实际占用的磁盘块大小,会小很多。df反映的是文件系统实际消耗的磁盘块,与du的默认行为一致。示例:一个 100GB 的虚拟机磁盘文件,可能只实际占用 10GB 空间。
du -h file显示10G,而ls -lh file显示100G。
原因五:文件系统元数据(Metadata)
原理:文件系统自身需要消耗磁盘空间来存储元数据,如 inode 表、磁盘位图、日志文件(对于日志文件系统如 ext3/4, xfs)等。
表现:这部分空间被文件系统占用,但不对应任何用户文件。因此
df会将其统计为“已用空间”,而du根本无法统计这些元数据,从而导致df的结果略大于du。
总结与对比
| 特性 | df | du |
|---|---|---|
| 数据源 | 文件系统超级块(元数据) | 递归遍历统计文件 |
| 统计视角 | 磁盘块分配情况 | 文件数据占用情况 |
| 速度 | 非常快(直接读元数据) | 相对慢(需要遍历文件树) |
| 包含保留空间 | 是 | 否 |
| 包含被删除但未释放的文件 | 是 | 否 |
| 包含稀疏文件“空洞” | 否(只算实际块) | 否(只算实际块,但 --apparent-size 算) |
| 包含元数据 | 是(元数据也占用块) | 否 |
实践建议
当遇到磁盘空间不足报警,但 du 找不到大文件时,排查思路如下:
首先执行
df -h,确认哪个分区使用率高。然后执行
du -sh /path/to/mountpoint,查看该分区下文件总大小是否与df结果近似。如果差异很大,首要怀疑有文件被删除未释放:使用
lsof +L1或lsof | grep deleted命令检查。其次考虑是否有隐藏挂载点、稀疏文件等原因。
云盘扩容后扩展分区操作
云主机的云盘从控制台100G扩容到300G后,根目录 /dev/vda1 显示还是100G,这通常是因为云盘扩容后只增加了底层存储空间,操作系统内部的分区和文件系统还未扩展。云平台的控制台扩容只是扩大了"物理磁盘"的容量,后续还需要在系统内部进行分区和文件系统的扩展。
1.确认底层磁盘容量
在操作系统内执行 lsblk 命令-4-5,它可以查看磁盘本身和分区的真实容量。
如果 lsblk 显示 /dev/vda 本身已经是300G,而其下的分区 /dev/vda1 还是100G,这就证实了是分区没有扩展-1-8。
2.扩展分区
确认是分区问题后,需要扩展分区以填满磁盘的可用空间。推荐使用 growpart 工具-5,它能够自动调整分区大小。
growpart /dev/vda 1
注意:命令中 vda 和 1 之间的逗号后有一个空格-5。完成后,再次运行 lsblk 检查,此时 /dev/vda1 的大小应该显示为300G左右。
3.扩展文件系统
分区扩展后,最后一步是扩展文件系统,这样才能让操作系统真正使用新增的空间。需要根据文件系统类型选择相应的命令。
如果文件系统是 ext4(常见于CentOS、Ubuntu等):
resize2fs /dev/vda1
如果文件系统是 XFS(常见于较新版本的CentOS、RHEL):
xfs_growfs /
注意:对于XFS文件系统,需要指定挂载点(此处根目录为 /),而不是设备名。
重要提醒与建议
在进行任何磁盘操作前,请务必注意以下几点:
命令匹配:务必确保扩展文件系统的命令与系统上的文件系统类型匹配,否则可能报错或无效-5。
参考: