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

62次阅读
没有评论

共计 4863 个字符,预计需要花费 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 目录下。

当 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 命令,所以速度较慢。

1. 如果统计目录下挂载了其他文件系统,那么也会对这个文件系统进行统计。例如 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 /

2. 如果文件被删除,即使被其他进程引用了,du 命令也无法对其统计。因为 stat 命令找不到这个文件。

3. 可以跨分区统计某些你想统计的文件大小总和。因为它们都能被 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 字节)。

1. 当某个文件系统下挂载了其他分区,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

2. 由于 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% /

3.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 的统计结果会不一样就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计4863字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)