写这篇文章的目的是:
可能有不少人都了解df和du不一致,可能是有deleted文件但fd还在的问题,但具体df和du是怎么统计磁盘分区大小的呢?你知道吗?
所以写下这篇文章,和大家一起分享下具体逻辑。
从一个简单的示例开始吧,我们使用/data0分区作为整篇文章的实验分区。
写入前,df查看/data0分区avial还有93G可用
现在在/data0分区下创建一个大小为1G的文件
写入后,df 查看/data0分区avial还有92G可用
然后写一个简单的脚本t.py,代码如下,功能只是打开a.log并死循环,每次sleep 10s
#!/usr/bin/env python
import os,sys
if __name__ == "__main__":
with open("./a.log", "r") as fp:
while True:
sleep(10)
在删除a.log之前我们df和du看下/data0分区的使用情况:
du的结果是已使用8.8G,df结果是已使用9G。
接下来删除a.log
删除之后,再使用df和du看下/data0的使用情况
df看到/data0的已使用空间是9G,和第一次df的结果一样;但du查看到/data0分区使用量却只有7.8G,和第一次相比正好少了1G,那1G就是a.log的大小,为什么删除了1G的文件,df统计出来的没有变化,但du的有变化,是什么原因呢?
接下来就带大家一起详细阐述这里的根因。
我一般比较喜欢把源码下载下来编译,然后走GDB,这样更快速的了解源码逻辑。
为了保持和现网版本一致,先查看下现网的使用版本
rpm –qa | grep –i coreutils |
在https://ftp.gnu.com/gun下载对应的coreutils源码进行编译即可
编译成功后,默认在src下生产对应的二进制文件,接下来可进行GDB。
获取本机文件系统列表
有人可能会问/etc/mtab是怎么得到的呢?其实/etc/mtab并不是在coreutils里定义的,而是在glibc里定义的,感兴趣的同学可以把glibc代码拉下来看下。
接下来看下du的过程
单步走到du_files函数
总结一下:
1、du主要是通过遍历文件,使用fts_read获取每个文件属性信息(包括文件大小、time和RWX属性等),从而计算所占磁盘空间大小
2、df主要是通过文件系统,调用statfs计算所占磁盘空间大小
3、df的statfs会根据文件系统类型调用对应的函数,内核支持的文件系统类型请查看对应版本的内核源码的fs/目录,该目录下列出了支持的文件系统类型。
其实我们lsof可以看到被delete的文件信息,文件已经不存在,但由于fd还在。因此df和du显示结果不一致,相差了正好是a.log的大小,即1G