引言
核心在于执行df和du的时候发现显示的存储量完全不同,我本地系统盘有99G空间,du显示占用了45G,但是df却显示使用了99G,排查的过程本文所示。
先记录几条大多数时候有用命令,看完文章就知道为什么有时候是不管用的了hhh
du -h / --exclude="data1" 2>/dev/null | grep '[0-9\.]\+G'
可以检查出目前/路径下目录的大小du -Sh ~ --max-depth=2 | sort -hr | head -10
可以递归的检查文件的大小find . -xdev -type f -size +100M
查找100M以上的文件find ~ -maxdepth 10 -type f -exec du -h {} + | sort -rh | head -10
最大文件top10
df/du 原理
从man page可以得到如下关键句:
df: df displays the amount of disk space available on the file system containing each file name argument.
du: Summarize device usage of the set of FILEs, recursively for directories.
从原理上讲,df是从super block直接读取该文件系统的元信息,简单的翻阅源码和执行下strace df -h
后不难发现df
实际是执行了statfs
,这个系统调用用于返回已经挂载的文件系统的元信息。
而du则完全不同,其原理是调用递归遍历目录,调用newfstatat系统调用获取文件信息,这允许 可以跨越多个文件系统统计大小,但是目录中文件很多时速度很慢。
[1][2]中描述df/du原理也很详细
排查思路
了解df/du原理后我们知道有三个情况可能这两种情况显示不同
文件系统预留空间
为了预防紧急情况,linux ext文件系统会预留部分硬盘空间,具体预留的数值可以通过tune2fs -l [dev_name] | grep "Reserved block count"
查看,这里预留的空间会被df计算到已用空间中,从而导致df和du统计不一致。如果需要调整预留空间大小,我们可以使用tune2fs -m [size] [dev_name]
来进行调整。
进程占用句柄
当一个文件被删除时,如果有别的进程正在使用它(占有句柄), 这个文件将不会被du统计到,但是这个文件被占用的磁盘空间却依然会被df统计到。这些文件,以及正在使用这些文件的进程可以通过lsof | grep deleted
查到。当进程停止或者被kill时,这些空间将被释放。 暴力的执行kill -9 $(lsof |grep -i deleted | awk '{print $2}' | sort -u)
可以删除这些进程,
挂载覆盖
当将一个目录挂在到一个新的设备(硬盘)上时,如果这个目录里面已经有数据,那么这一部分数据不会被遍历到,自然也不会被du感知,在文件系统中也看不到这些数据,但是这些数据又是确实占用了磁盘空间,是能够被df所统计到的。这时候通过du/df统计原设备的空间使用情况,就会发现df统计到的比du要多。
因为我把数据盘挂载在了根目录中,解决的方案就是unmount后删除这部分目录,我在这部分释放了15G空间。
umount的时候可以会遇到busy的情况,
- 因为代码都在挂载的盘中,首先关闭vscode的所有终端
- 其次镜像存储都在挂载的盘中,关闭minikube:
minikube stop; minikube delete
- 关闭 docker:
systemctl stop docker
- 再检查与挂载设备关联的进程:
lsof /data1
参考:
- http://sysunconfig.net/aixtips/df_du_diff_out.txt
- https://www.quora.com/What-is-the-difference-between-DU-and-DF-in-Linux
- umount卸载磁盘提示target is busy. (目标忙) 的问题解决方案