Linux的du和df命令

这是4月18号的流水账

今天如往常一样完成一个功能后,把代码推到测试环境,结果 response 有点跟往常不一样:

1
2
3
4
5
6
7
8
9
10
11
12
liubin@localhost:~# git push test branch
Counting objects: 18, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (16/16), done.
Writing objects: 100% (18/18), 2.12 KiB | 0 bytes/s, done.
Total 18 (delta 11), reused 0 (delta 0)
remote: error: file write error (No space left on device)
remote: fatal: unable to write sha1 file
error: unpack failed: unpack-objects abnormal exit
To csrtest:/var/gitlab/test.git
! [remote rejected] branch -> branch (unpacker error)
error: failed to push some refs to 'git@test:/var/gitlab/test.git'

No space left on device. 意思是测试服务器上没空了呗,上去看一下:

1
2
3
4
5
6
liubin@test:~# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda2 289G 274G 16M 100% /
tmpfs 939M 0 939M 0% /dev/shm
/dev/vda1 985M 93M 842M 10% /boot
/dev/vda3 203G 188M 193G 1% /data

果不其然,那应该是缓存文件太多了,去缓存目录看一下:

1
2
liubin@test:~# du -sh
184G

那清一清吧。

1
2
3
4
liubin@test:~# rm -rf ./*
zsh: sure you want to delete all the files in .../cache/. [yn]? y
liubin@test:~# ll
total 0

清完然后就好使了。

可是清完后跟之前的数有点对不大上:

1
2
3
4
5
6
liubin@test:~# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda2 289G 208G 66G 76% /
tmpfs 939M 0 939M 0% /dev/shm
/dev/vda1 985M 93M 842M 10% /boot
/dev/vda3 203G 188M 193G 1% /data

我猜可能是虚拟机的存储管理机制的缘故,物理机出现这种情况的话那才奇了怪了,不过我也没兴趣继续深究这个问题。

就是记一次流水账,非科普。

需要科普可以去查 Linux命令大全

以下是4月19号的补充

本以为今天和往常一样,结果跟昨天一样。

测试服务器磁盘又满了。

隐约觉得应该是哪儿溢出了,top 一下果然 php 进程始终99%,又看了下php进程,有一个执行后台计算的脚本,从4月17号下午就已经存在了,sudo kill -9 了之后,结果 df -h 对上了!

1
2
3
4
5
6
liubin@test:~# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda2 289G 91G 184G 34% /
tmpfs 939M 0 939M 0% /dev/shm
/dev/vda1 985M 93M 842M 10% /boot
/dev/vda3 203G 188M 193G 1% /data

然后把那个“溢出”的进程在本地复现了之后,想起了昨天的那句:

物理机出现这种情况的话那才奇了怪了。

所以说,还是看看怎么给虚拟机平反吧。毕竟,真相只有一个!

果不其然,一查就找到了真相,有以下三种情况:

1.预留空间

为了预防紧急情况,linux ext文件系统会预留部分硬盘空间,具体预留的数值可以通过 tune2fs -l [dev_name] | grep "Reserved block count" 查看到(dev_name是设备名),这里预留的空间会被 df 计算到已用空间中,从而导致 dfdu 统计不一致。如果需要调整预留空间大小,我们可以使用 tune2fs -m [size] [dev_name] 来进行调整。

2.幻影文件(phantom file)

du是统计被文件系统记录到的每个文件的大小,然后进行累加得到的大小,这是通过文件系统获取到的。而 df 主要是从超级块(superblock)中读入硬盘使用信息, df 获取到的是磁盘块被使用的情况。当一个文件被删除时,如果有别的进程正在使用它(占有句柄),
这个文件将不会被 du 统计到,但是这个文件被占用的磁盘空间却依然会被 df 统计到。这些文件,以及正在使用这些文件的进程可以通过 lsof | grep deleted 查到。当进程停止或者被 kill 时,这些空间将被释放。

3.未统计到的文件

如果上面两种情况都排除了,但是数据还是不一致,那是怎么回事?这里隐藏着一种情况:当我们将一个目录挂在到一个新的设备(硬盘)上之前,如果这个目录里面已经有数据,那么这一部分数据不会被 du 感知,在文件系统中也看不到这些数据,但是这些数据又是确实占用了磁盘空间,是能够被 df 所统计到的。这时候通过 du / df 统计原设备的空间使用情况,就会发现 df 统计到的比 du 要多。遇到这样的情况时,使用 fuser -km [directory] 杀死占用该目录的所有进程(小心操作!),然后使用 umount [directory] 将该目录挂载的设备卸载,这时,目录里面原来已有的数据就会出现,我们将其删除之后,再重新挂载设备(mount -t [type] [dev] [directory])即可。

而发生在我这里的就是第二种情况,文件被删除后,句柄还在被进程占用着,所以导致 dudf 意见不统一,kill 掉占用进程后,统计结果就一致了。

本想写篇流水账凑数,结果还是被强行 push 到探索真相的道路上。

题外话

上面说那个“溢出”的进程加引号的原因是,表面上看起来像是溢出的进程,其实是产品经理那天添加了一个计算结果超过100亿行的计算任务,可能等了半天也没见出结果,就把那条记录给删掉了… 然后测试机就一两核 cpu & 2G 内存的虚拟机,两天也没运行完,所以就造成了看似溢出的假象。