分析df和du的区别

时间:2021-03-13 05:25:21

  Linux、Unix-Like平台的用户都可能会发现,du与df的查询结果会不一致。最常见的情况就是,df显示的已使用磁盘占用率比du统计出来的结果要大很多。

  下面说下两者结果不同的原因,主要是由于两者计算结果的方式不同。

  先来看看两者是如何计算的

  df命令:

  df是从文件系统考虑,通过文件系统中未分配的空间来确定文件系统中已分配空间的大小。例如:如果一个文件系统中有8192个512-byte 块,并且4096个块没有被分配出去,那么已分配的空间就是4096个512-byte 的块。

  已分配空间=空间总数-未分配空间

  df中的used还包含了程序申请的、被程序占用的空间等等。因为基于文件系统总体来计算,所以df命令是报告文件系统空间使用情况最可靠的命令。

  du命令

  du是面向文件的命令,它计算分配给指定文件或者目录的空间。du命令必须跟着目的参数,而且不能隔离文件系统。

  例如:运行 #du /命令将计算所有在/文件系统下所有文件的空间分配信息。其中包括/文件系统所有的文件和安装在/下面的,例如/tmp, /var和/usr下面的其他文件系统的文件。可以用du命令带-x参数来限制仅在文件系统内进行操作。但是有时候会导致输出结果不完全。

  du命令只计算被文件占用的空间。不计算文件系统metadata 占用的空间,如inodes, inode maps, 或者disk maps。

  du命令只计算那些可以访问的文件所占用的存储空间。

  下面两种情况du命令不会计算已经分配给文件的空间。

  Case1:

  文件被隐藏了。

  例如:如果一个文件存放在/bobby 目录下, 接着有文件系统安装(mount)在/bobby目录下, 那么du 命令将不会计算 /bobby 目录下的文件所占用的存储空间。

  Case2:

  文件被其他的应用的打开了,接着文件被删除了(rm命令删除)。 在这种情况下,文件所占用的存储空间将维持着被分配的状态直到所有对这个文件的访问都被关闭。由于目录中没有这个文件的相关纪录,du命令将不会计算这个文件的被分配空间,但df命令将计算这部分已分配的空间。

  除rm外,有些不明显的操作也会产生类似的问题。

  例如gzip命令,其对某个文件xxx.log进行压缩时,会产生一个新的xxx.log.gz文件,完成后,会把原来的xxx.log删除。这时,若仍有进程在使用xxx.log文件。那么,实际上该文件还是只会标记为deleted,其空间也不会释放,问题与上面提到的情况是相同的。所以,在编写脚本时,可先判断是否仍有进程正在使用该文件,然后再进行gzip操作。

  通俗点讲,du就是遍历某个目录,把所有文件的大小加起来,都是那看得见的东西的和

  简单的模拟测试实验(以下实验属引用,未测试。看了下输出,结果应该没问题。呵呵!)

  实验情况

  1、创建并删除文件

  创建文件前的磁盘容量情况:

?View Code BASH
1
2
3
4
# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda1 12G 5.7G 5.5G 51% /
tmpfs 506M 0 506M 0% /dev/shm

  创建文件:

?View Code BASH
1
2
3
4
# dd if=/dev/zero of=test.iso bs=1024k count=1000
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB) copied, 14.3055 seconds, 73.3 MB/s

  现在的磁盘情况:

?View Code BASH
1
2
3
4
# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda1 12G 6.7G 4.6G 60% /
tmpfs 506M 0 506M 0% /dev/shm

  模拟某个进程正在使用该文件:

?View Code BASH
1
# tail -f /tmp/test.iso

分析df和du的区别

  2、删除该文件

  打开另一个终端,登陆到系统中。查看是否有进程正在使用上面创建的文件:

?View Code BASH
1
2
# lsof |grep test.iso
tail 2175 root 3r REG 8,1 1048576000 752972 /tmp/test.iso

  把该文件删掉,并确认:

?View Code BASH
1
2
3
4
# rm /tmp/test.iso
rm:是否删除 一般文件 “/tmp/test.iso”? y
# ls /tmp/test.iso
ls: /tmp/test.iso: 没有那个文件或目录

  查看是否还有进程在使用(注意结尾的标记):

?View Code BASH
1
2
# lsof |grep test.iso
tail 2175 root 3r REG 8,1 1048576000 752972 /tmp/test.iso (deleted)

  查看磁盘使用情况:

?View Code BASH
1
2
3
4
5
6
7
# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda1 12G 6.7G 4.6G 60% /
tmpfs 506M 0 506M 0% /dev/shm
 
# cat /proc/diskstats |grep sda1
8 1 sda1 54385 5184 1626626 130090 20434 635997 5251448 5345733 0 111685 5475829

  可见,虽然从ls已经无法找到该文件,但因为tail进程仍在使用该文件,故实际上内核并没有把这文件所占用的空间释放出来(df的结果)。

  3、停止相关进程

  回到第一终端,用Ctrl+C 终止tail 进程,查看结果:

?View Code BASH
1
2
3
4
5
6
7
# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda1 12G 5.7G 5.5G 51% /
tmpfs 506M 0 506M 0% /dev/shm
 
# cat /proc/diskstats |grep sda1
8 1 sda1 54473 5184 1627402 130617 20453 636042 5251960 5345756 0 112226 5476379

  至此,文件所占用的空间已完全释放。

  从上面的实验,可得出一些情况:

  1、若有进程在占用某个文件,而其他进程把这文件删掉,只会删除其在磁盘中的标记,而不会释放其占用的磁盘空间;直到所有访问该文件的进程退出为止;
  2、df是从内核中获取磁盘占用情况数据的,而du是统计当前磁盘文件大小的结果,由于磁盘标记已被删掉,因此du 不会计算上述被删除文件的空间,导致df 与 du的结果不一致。

  解决问题

  通常的解决方法有两个

  1、把占用文件的相关进程关闭

  这可通过下面的命令得到这些已被删除,但未释放空间的文件和进程信息:

?View Code BASH
1
# lsof |grep deleted

  找到这些进程后,在安全的情况下把其关闭,空间自会马上释放。

  2、以清空的方式替代删除

  归根到底,产生问题的原因是,访问该文件的文件指针(句柄),在rm动作后,因为进程仍在访问,因此,仍处在文件里面(中间或结尾处)。所以,如果用清空的方式,把文件指针重置,该文件所占用的空间也会马上释放出来。

?View Code BASH
1
2
3
4
5
6
7
8
9
# echo > /tmp/test.iso
 
# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda1 12G 5.7G 5.5G 51% /
tmpfs 506M 0 506M 0% /dev/shm
 
# tail -f /tmp/test.iso
tail: /tmp/test.iso: file truncated

  所以,对于常发生类似问题的文件,如:日志记录文件等。以改名、清空、删除的顺序操作可避免问题。

  参考文档

  http://www.linuxfly.org/post/575/
  http://hi.baidu.com/ioah/blog/item/5792bc4c8d9cc0f2d62afc6e.html

转载自:http://www.mike.org.cn/articles/analysis-of-the-difference-df-and-du/