目录
使用场景
- du:用于查看文件路径的空间使用情况
- df:用于查看盘的空间使用情况
原理
1.df工作原理
对待统计文件逐个调用fstat这个系统调用,获取文件大小。
它的数据是基于文件获取的,所以有很大的灵活性,不一定非要针对一个分区,可以跨越多个分区操作。
如果针对的目录中文件很多,du速度就会很慢了。
2.du工作原理
使用statfs这个系统调用,直接读取分区的超级块信息获取分区使用情况。
它的数据是基于分区元数据的,所以只能针对整个分区。
由于df直接读取超级块,所以运行速度不受文件多少影响。
3.区别
df通过文件系统来快速获取空间大小的信息,当我们删除一个文件的时候,这个文件不是马上就在文件系统当中消失了,而是暂时消失了,当所有程序都不用时,才会根据OS的规则释放掉已经删除的文件, df记录的是通过文件系统获取到的文件的大小,他比du强的地方就是能够看到已经删除的文件,而且计算大小的时候,把这一部分的空间也加上了,更精确了。
du -s
命令通过将指定文件系统中所有的目录、符号链接和文件使用的块数累加得到该文件系统使用的总块数;
df命令通过查看文件系统磁盘块分配图得出总块数与剩余块数。文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如i节点,磁盘分布图,间接块,超级块等。这些数据对大多数用户级的程序来说是不可见的,通常称为Meta Data。
du命令是用户级的程序,它不考虑Meta Data,而df命令则查看文件系统的磁盘分配图并考虑Meta Data。
df命令获得真正的文件系统数据,而du命令只查看文件系统的部分情况。
如果用户删除了一个正在运行的应用所打开的某个目录下的文件,则du命令返回的值显示出减去了该文件后的目录的大小。但df命令并不显示减去该文件后的大小。
直到该运行的应用关闭了这个打开的文件,df返回的值才显示出减去了该文件后的文件系统的使用情况。当文件系统也确定删除了该文件后,这时候du与df就一致了。
df和du显示的数据不一致的情况
数据不一致的情况:
用户删除了大量的文件后,du命令就不会在文件系统目录中统计这些文件。如果此时还在运行中的进程持有这个已经被删除的文件句柄,那么这个文件就不会真正在磁盘中被删除,分区超级块中的信息也就不会更改,df命令仍会统计这个被删除的文件。通过lsof命令查询处于deleted状态的文件,被删除的文件在系统中被标记为deleted。如果系统有大量deleted状态的文件,会导致du和df命令统计结果不一致。
建议:
1.根据lsof列出的进程号,终止相应进程或者重启相应的服务。也可以重启实例,重启实例系统会退出现有的进程,开机后重新加载过程中,会释放调用的deleted文件的句柄。
2.Linux系统磁盘分区有保留区的概念,会给root或指定用户预留5%或更大的空间,当使用到这块保留区的空间时,fdisk
命令的计算将会是负数。ext文件系统(包括ext2、ext3、ext4)都会默认预留5%的磁盘空间,使用root用户维护系统或记录系统关键日志使用。
3.当用du -sh *
命令来统计目录总容量时,如果该路径下包含隐藏文件,是不会包含在统计结果里的。
4.由于数据盘挂载前该路径下就存在文件,挂载后用du
无法查询到原路径文件。
1.df比du大
第一种情况:
用户删除了大量的文件后,du命令就不会在文件系统目录中统计这些文件。如果此时还在运行中的进程持有这个已经被删除的文件句柄,那么这个文件就不会真正在磁盘中被删除,分区超级块中的信息也就不会更改,df命令仍会统计这个被删除的文件。 通过lsof命令查询处于deleted状态的文件,被删除的文件在系统中被标记为deleted。如果系统有大量deleted状态的文件,会导致du和df命令统计结果不一致。
解决方法:
1.用lsof命令列出被程序所打开的文件文件名,并筛选出被删除的文件:
lsof | grep deleted > deleted_file
2.将文件按从大到小排序:
sort -nr -k 7 deleted_file > deleted_file_sorted
打开deleted_file_sorted文件,从上往下依次找,就可以看到一些被删除但是没有释放的文件,以及占用这些文件的程序。
top之后若却看不到相关的进程,因为top看到的是当前用户和系统的一些活跃进程,那些僵尸进程可能就显示不出来了。
解决办法:是用top -u 用户名
,仅查看本用户的进程,就可以看到了,然后kill掉进程,文件就被释放了。
第二种情况:
元数据Meta Data的存在:
du命令是用户级的程序,它不考虑元数据。
df命令则查看文件系统的磁盘分配图因此考虑了元数据,所以df的返回值可能会因此大于du。
文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如i节点,磁盘分布图,间接块,超级块等。
这些数据对大多数用户级的程序来说是不可见的,通常称为Meta Data。
2.df比du小
场景:使用docker,kubernetes等云原生环境
现象:df看到的已使用空间比du看到的已使用空间小很多
原因:使用docker镜像的时候,其实底层overlay文件系统是层级复用的原理,所以真正使用的磁盘空间并不会重复叠加,但是du看到的是会叠加的,因为对overlay文件系统路径进行du的时候,显示的是带覆盖的完整的使用空间。
# df
luzejia@luzejia-virtual-machine:~$ df -h
文件系统 容量 已用 可用 已用% 挂载点
tmpfs 1.6G 2.1M 1.6G 1% /run
/dev/sda3 491G 16G 451G 4% /
tmpfs 7.8G 0 7.8G 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/sda2 512M 5.3M 507M 2% /boot/efi
tmpfs 1.6G 4.7M 1.6G 1% /run/user/1000
/dev/sr0 127M 127M 0 100% /media/luzejia/CDROM
/dev/sr1 3.5G 3.5G 0 100% /media/luzejia/Ubuntu 22.04 LTS amd64
# 启动一个容器
luzejia@luzejia-virtual-machine:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e107632809ca ubuntu "bash" About an hour ago Up About an hour ubuntu-test
# 再df的时候,多显示了一个挂载目录,挂载到了overlay文件系统下
luzejia@luzejia-virtual-machine:~$ sudo df -h
文件系统 容量 已用 可用 已用% 挂载点
tmpfs 1.6G 2.2M 1.6G 1% /run
/dev/sda3 491G 16G 451G 4% /
tmpfs 7.8G 0 7.8G 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/sda2 512M 5.3M 507M 2% /boot/efi
tmpfs 1.6G 4.7M 1.6G 1% /run/user/1000
/dev/sr0 127M 127M 0 100% /media/luzejia/CDROM
/dev/sr1 3.5G 3.5G 0 100% /media/luzejia/Ubuntu 22.04 LTS amd64
overlay 491G 16G 451G 4% /var/lib/docker/overlay2/fa666512fdebeaf8d6fa3611f248dd1d04a3be086dd256168e61166d89b846f9/merged
# 此时你du看这个路径,发现有168M的大小,
luzejia@luzejia-virtual-machine:~$ sudo du -BM /var/lib/docker
168M /var/lib/docker
然而实际对比启动容器前后,df是没有明显变化的,因为服用了盘上的镜像层,所以盘的使用空间没有明显变化,但是挂载的这个overlay文件系统下的路径,你用du去看,就会给你显示完整的占用空间,层之间虽然是复用,但是也会给你展示完整的占用空间。
查看docker真正占用的磁盘空间
luzejia@luzejia-virtual-machine:~$ sudo docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 77.82MB 0B (0%)
Containers 1 1 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
查看容器修改了哪些文件
docker container diff containerId
du查看隐藏文件
执行du -ah
命令即可在统计结果中包含隐藏文件。
为什么overlay文件系统路径用du看到的是完整的带覆盖的占用,而非实际使用
猜测:
容器如果不做修改那么不会有额外的磁盘空间使用,当你修改的时候,会将修改做在overlay的读写层,那么假设你有磁盘1G,每个容器使用100M,但是基于同一个镜像的容器空间是复用的,如果不做修改的话,那么此时你可以启动非常多个,然后实际显示的是100M多一点,也就是底本多一点元数据的空间,但是容器使用的是copy on write,当写的时候就复制一个新的来写,读的时候复用,那么当大家都需要写的时候,那么你此时磁盘空间可能就不够了,但是如果一开始就把复用的这些空间先占起来,那么就使得当大家都需要写的时候,也是有空间的,相当于预留的思路。
相同的设计:
Kuberntes中的request请求,当你request之后,资源就被你申请走了,此时哪怕你不用,也会给你预留,以防止大家都需要使用的时候,发生资源争抢。