阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

详细分析du和df的统计结果为什么不一样

31次阅读
没有评论

共计 5180 个字符,预计需要花费 13 分钟才能阅读完成。

导读 使用 du 和 df 来获取目录或文件系统已占用空间的情况。但它们的统计结果是不一致的,大多数时候,它们的结果相差不会很大,但有时候它们的统计结果会相差非常大。

详细分析 du 和 df 的统计结果为什么不一样

df 的统计结果
[root@xuexi ~]# df -hT
Filesystem          Type   Size  Used Avail Use% Mounted on
/dev/sda2           ext4    18G  1.7G   15G  11% /
tmpfs               tmpfs  491M     0  491M   0% /dev/shm
/dev/sda1           ext4   239M   68M  159M  30% /boot
//192.168.0.124/win cifs   381G  243G  138G  64% /mnt
du 对根目录的统计结果
[root@xuexi ~]# du -sh /  2>/dev/null
244G    /
df 中 "/" 的使用空间是 1.7G,但是 du 的结果却是 244G。这里 du 的统计结果大于 df。再看看对 /boot 分区的统计结果。[root@xuexi ~]# df -hT /boot;echo;du -sh /boot
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda1      ext4  239M   68M  159M  30% /boot
66M     /boot

du 的结果是 66M,df 的结果是 68M,相差不大,但 df 的结果大于 du。

文件存储和删除的底层过程

这里简单说明下文件系统相关的底层机制,首先说明下文件是怎么存储到文件系统中的。假如要存储 a.txt 到 /tmp 目录下。
详细分析 du 和 df 的统计结果为什么不一样
当 a.txt 文件要存储到 /tmp 下时:

  1.  首先从 inode table 中找一个空闲的 inode 号分配给 a.txt,例如 2222。再将 inode map(imap)中 2222 这个 inode 号标记为已使用。
  2.  在 /tmp 的 data block 中添加一条 a.txt 文件的记录。该记录中包括一个指向 inode 号的指针,例如 ”0x2222″。
  3.  然后从 block map(bmap)中找出空闲的 data block,并开始将 a.txt 中的数据写入到 data block 中。每写一段空间 (ext4 每次分配一段空间) 就从 bmap 中找一次空闲的 data block,直到存完所有数据。
  4.  设置 inode table 中关于 2222 这条记录的 data block 指针,通过该指针可以找到 a.txt 使用了哪些 data block。

当要删除 a.txt 文件时:

  1.  在 inode table 中删除指向 a.txt 的 data block 指针。这里只要一删除,外界就找不到 a.txt 的数据了。但是这个文件还存在,只是它是被 ” 损坏 ” 的文件,因为没有任何指针指向数据块。
  2.  在 imap 中将 2222 的 inode 号标记为未使用。于是这个 inode 号就被释放,可以被后续的文件重用。
  3.  删除父目录 /tmp 的 data block 中关于 a.txt 的记录。这里只要一删除,外界就看不到也找不到这个文件了。
  4.  在 bmap 中将 a.txt 占用的 block 标记为未使用。这里被标记为未使用后,这些 data block 就可以被后续文件覆盖重用。

考虑一种情况,当一个文件被删除时,但此时还有进程在使用这个文件,这时是怎样的情况呢?外界是看不到也找不到这个文件的,所以删除的过程已经进行到了第 (3) 步。

但进程还在使用这个文件的数据,也能找到这个文件的数据,是因为进程在加载这个文件的时候就已经获取到了该文件占用哪些 data block,虽然删除了文件,但 bmap 中这些 data block 还没有标记为未使用。

du 统计的原理

du 是通过 stat 命令来统计每个文件 (包括子目录) 的空间占用总和。因为会对每个涉及到的文件使用 stat 命令,所以速度较慢。
如果统计目录下挂载了其他文件系统,那么也会对这个文件系统进行统计。例如 ”du -sh /” 的时候,会统计所有分区的文件,包括挂载上来的。正如本文开头统计的 ”/” 一样,du 的结果是 244G,明显比 df 统计的结果大,就是因为将某个分区挂载到了 /mnt 目录下。

## df 的统计结果
[root@xuexi ~]# df -hT
Filesystem          Type   Size  Used Avail Use% Mounted on
/dev/sda2           ext4    18G  1.7G   15G  11% /
tmpfs               tmpfs  491M     0  491M   0% /dev/shm
/dev/sda1           ext4   239M   68M  159M  30% /boot
//192.168.0.124/win cifs   381G  243G  138G  64% /mnt## du 对根目录的统计结果
[root@xuexi ~]# du -sh /  2>/dev/null
244G    /

如果文件被删除,即使被其他进程引用了,du 命令也无法对其统计。因为 stat 命令找不到这个文件。
可以跨分区统计某些你想统计的文件大小总和。因为它们都能被 stat 找到并统计。例如:统计 Linux 下所有 img 文件的大小。

## df 的统计结果
[root@xuexi ~]# find / -type f -name "*.img" -print0 | xargs -0 du -csh
19M     /boot/initramfs-2.6.32-504.el6.x86_64.img
13M     /mnt/linux 工具 /cirros-0.3.4-x86_64-disk.img
31M     total

这里统计的两个 img 文件就是在不同分区内的。

df 统计的原理

df 是读取每个分区的 superblock 来获取空闲数据块、已使用数据块,从而计算出空闲空间和已使用空间,因此 df 统计的速度极快(superblock 才占用 1024 字节)。
当某个文件系统下挂载了其他分区,df 不会把这个分区也统计进去。这很容易理解,因为 df 读取的是各自分区的 superblock,即使分区 1 挂载在分区 0 的目录下,df 统计分区 0 的时候,也只能读取分区 0 的 superblock。
例如,下面的 /mnt、/boot 都没有统计在 ”/” 中。

[root@xuexi ~]# df -hT
Filesystem          Type   Size  Used Avail Use% Mounted on
/dev/sda2           ext4    18G  1.7G   15G  11% /
tmpfs               tmpfs  491M     0  491M   0% /dev/shm
/dev/sda1           ext4   239M   68M  159M  30% /boot
//192.168.0.124/win cifs   381G  243G  138G  64% /mnt

由于 df 每次统计都是读取 superblock,所以 df 对文件系统中的某个文件进行统计时,会自动转为统计这个文件系统的信息。

[root@xuexi ~]# df -hT /etc/fstab
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda2      ext4   18G  1.7G   15G  11% /

df 会统计已删除但却仍有进程引用的文件。

正常情况下,删除文件会立刻释放相关指针,并将 imap 和 bmap 中相关的位图标记为未使用。bmap 只要一改变,文件系统立刻就能知道每个块组中哪些数据块是空闲的,哪些数据块是被使用的,这些信息都会更新到分区的 superblock 中。于是 df 能立刻统计到实时的空间信息。

但是当一个文件被删除时,如果还有进程在引用这个文件,根据前文的分析,bmap 中不会将这个文件的 data block 标记为未使用,也就不会将数据块的使用情况更新到 superblock 中。由于 df 是根据 superblock 中空闲和使用数据块的数量来计算空闲空间和已使用空间的,所以 df 统计的时候会将这个已被 ” 删除 ” 的文件统计到已使用空间中。

例如,创建一个较大一点的文件放在 ”/” 目录下,并 du 和 df 统计根目录的已使用空间。

[root@xuexi ~]# dd if=/dev/zero of=/my.iso bs=1M count=1000
[root@xuexi ~]# df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda2      ext4   18G  2.7G   14G  17% /
[root@xuexi ~]# du -sh --exclude="/mnt" / 2>/dev/null
2.7G    /

它们在 GB 级的单位上是相等的。现在使用一个进程来引用这个文件,然后删除这个文件,再 du 和 df 统计。

[root@xuexi ~]# tail -f /my.iso &
[root@xuexi ~]# rm -rf /my.iso
[root@xuexi ~]# ls /my.iso
ls: cannot access /my.iso: No such file or directory
[root@xuexi ~]# du -sh --exclude="/mnt" / 2>/dev/null
1.8G    /
[root@xuexi ~]# df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda2      ext4   18G  2.7G   14G  17% /

可以发现,外界已经获取不到 my.iso 文件了,所以 du 无法统计这个文件。而 df 却将该文件大小统计进去了,因为 my.iso 占用的 data block 还未被标记为未使用。再关掉 tail 进程,然后 df 再统计空间,结果将和 du 一样显示为正常的大小。

[root@xuexi ~]# jobs
[1]+  Running                 tail -f /my.iso &
[root@xuexi ~]# kill %1
[root@xuexi ~]# df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda2      ext4   18G  1.7G   15G  11% /

如果不知道文件系统中哪些已被删除,但却还被进程引用的文件,可以使用 lsof 来获取。通过它还能获取到文件的大小,看看到底是哪个文件在 ” 占着茅坑以及占了多少茅坑 ”。例如,关掉 tail 进程前,使用 lsof 查看。可以看到 tail 进程占用了 /my.iso,且这个文件的大小为 1048576000 字节。

[root@xuexi ~]# lsof | grep deleted
php-fpm   12597      root  txt     REG   8,2    4058416   931143 /usr/sbin/php-fpm (deleted)
php-fpm   12657    nobody  txt     REG   8,2    4058416   931143 /usr/sbin/php-fpm (deleted)
php-fpm   12707    nobody  txt     REG   8,2    4058416   931143 /usr/sbin/php-fpm (deleted)
php-fpm   12708    nobody  txt     REG   8,2    4058416   931143 /usr/sbin/php-fpm (deleted)
tail      14437      root    3r    REG   8,2 1048576000     7171 /my.iso (deleted)

经过上面的分析,想必对 du 和 df 的结果不会再有任何疑惑了吧。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计5180字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中