如何处理df和du输出不一致的问题

标准GNU coreutils工具包含dfdu用于查看磁盘的使用情况。通常情况下,两个工具显示的磁盘空间是基本相同的,但用户经常会遇到两者输出差异很大的情况。本文将介绍导致两者不一致的常见原因及解决方案。

dfdu命令说明

dfdu命令的统计逻辑本身存在较小的差异。

df显示文件系统的磁盘空间使用情况。使用statfs()系统调用来统计这些信息,而 statfs() 从文件系统的超级块superblock中读取数据。默认情况下,df输出为1 KB block,您可以通过参数-B--block-size=SIZE指定不同block的输出大小。

特点:反映文件系统级别的空间使用情况,包括未被文件引用但尚未释放的空间(例如,已删除的文件仍被进程所占用)。

du显示文件和目录的磁盘空间使用情况。du命令通过深度优先遍历目标文件系统目录下的所有文件和目录,使用stat()系统调用来统计,stat()读取每个文件的大小,默认单位为KB。

特点:反映文件和目录的实际占用情况,未包含未被文件引用的空间。

综上可知,两个命令的统计逻辑本身存在差异,但一般情况下不会导致较大差异。

常见问题

Q1:存在已删除但未释放的文件

问题现象

df显示磁盘空间不足,但du统计的总大小明显低于df显示的已使用空间。

问题原因

某些文件已被删除,但仍有进程持有这些文件的文件描述符(file descriptor)。文件系统在所有引用该文件的进程关闭文件描述符之前,无法回收相应的空间。

执行以下命令,使用lsof查看所有已删除但仍然被进程占用的文件。

sudo lsof | grep deleted

解决方案

在可以终止对应进程的前提下,使用kill命令终止对应进程,从而释放文件描述符。

<PID>需替换为lsof命令列出的进程ID。

sudo kill -9 <PID>

Q2:子目录被挂载到其他文件系统

问题现象

du挂载点返回的结果与df返回的结果存在较大差异。

问题原因

如果一个挂载点的子目录被其他文件系统再次挂载,du 命令对父挂载点目录的统计将返回最新的目录树遍历统计信息,而 df 命令仍然返回原挂载点的使用情况。

例如,/dev/vdb挂载点为/mnt/vdb,已使用空间为30 G,同时在/mnt/vdb/tmp目录中存在一个1 G的文件。一个新设备/mnt/vdc被挂载到/mnt/vdb/tmp,并且该设备已使用5.1 G的空间。

  1. 执行以下命令,查看 /mnt/vdb 挂载点的挂载信息。

    sudo mount | grep /mnt/vdb

    结果如下所示。

    /dev/vdb         40G   30G   11G  75% /mnt/vdb
    /dev/vdc         40G  5.1G   33G  14% /mnt/vdb/tmp
  2. 执行以下命令,查看/mnt/vdb目录的磁盘使用情况。

    du -hs /mnt/vdb

    结果如下所示,返回替换/mnt/vdb/tmp后的统计值。但df仍然是原/mnt/vdb的统计值。

    34G     /mnt/vdb

解决方案

如果存在覆盖挂载,执行以下命令,卸载相应的文件系统,确保dfdu目标文件系统一致。

sudo umount /mnt/vdb/tmp