df命令,接触过Linux/Unix的人都用过吧?不过为什么有的数字总是算不对呢?
例如下面,/data那个文件系统,
total = 1404203532
used = 1203335028
available = 129539124
use% = 91%
但是,total - used = 200868504 <> available
而且,used / total = 86% <> use%
- -bash-3.00$ df
- Filesystem 1K-blocks Used Available Use% Mounted on
- /dev/mapper/vg00-lvol00
- 2031952 187976 1740760 10% /
- /dev/sda2 497861 17285 454872 4% /boot
- /dev/mapper/vg00-lvol05
- 1404203532 1203335028 129539124 91% /data
- none 2074584 0 2074584 0% /dev/shm
- /dev/mapper/vg00-lvol04
- 10095152 6955380 2626956 73% /home
- /dev/mapper/vg00-lvol02
- 10095152 2091108 7491228 22% /usr
- /dev/mapper/vg00-lvol03
- 10095152 229120 9353216 3% /var
- apams01-vip1:/mnt/mdraw1
- 2097015808 1785570528 311445280 86% /nas_mdraw
然而,/nas_mdraw那个文件系统的统计结果又是对的。2097015808 - 1785570528 = 311445280,1785570528 / 2097015808 = 86%
因此,df的计算方法到底如何,显得有点诡异。不过,本文也不准备卖关子,正确答案是:
df命令的输出清单的第1列是代表文件系统对应的设备文件的路径名(一般是硬盘上的分区);第2列给出分区包含的数据块(1024字节)的数目;第3,4 列分别表示已用的和可用的数据块数目。用户也许会感到奇怪的是,第3,4列块数之和不等于第2列中的块数。这是因为缺省的每个分区都留了少量空间供系统管 理员使用。即使遇到普通用户空间已满的情况,管理员仍能登录和留有解决问题所需的工作空间。清单中 Use% 列表示普通用户空间使用的百分比,即使这一数字达到100%,分区仍然留有系统管理员使用的空间。最后,Mounted on列表示文件系统的安装点。
而这个缺省的预留空间,默认值是5%,这部分空间,普通用户不能使用,上面的/data文件系统就是这种情况,91%-86%=5%;另外,在root创 建文件系统的时候是可以改变其大小的,如果mkfs的时候设置预留空间为0,那么普通用户将能够使用该设备的所有空间,上面的/nas_mdraw文件系 统就是这种情况。
如果本文到此为止的话,那么没有多大实质意义,网上相关的解释到处都是,甚至上面整段话都是来自搜索引擎的。于是,我们从df的源代码用到的结构体开始,简单分析一下df的算法,利用其开源的接口,逐步写出一个综合df、du等功能的统计文件系统内各目录使用情况的程序。
首先介绍一下,df的源代码在Coreutils包中,Linux的许多基本命令,例如ls、cp等等,都包含在这个包中。大家可以到GNU的官方网站下载最新版本:
http://www.gnu.org/software/coreutils/
下载解包之后,很容易就能够在src目录找到df.c。简单跟踪一下的话,会发现df在输出结果之前会将读到的数据写到一个struct fs_usage结构体中,这个结构体在lib目录下的fsusage.h中定义:
- struct fs_usage
- {
- uintmax_t fsu_blocksize; /* Size of a block. */
- uintmax_t fsu_blocks; /* Total blocks. */
- uintmax_t fsu_bfree; /* Free blocks available to superuser. */
- uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
- bool fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
- uintmax_t fsu_files; /* Total file nodes. */
- uintmax_t fsu_ffree; /* Free file nodes. */
- };
大部分定义看英文描述就足够清晰了,除了那个bool类型成员。不过,我调试的时候还真没见过其取值为1的情况,于是暂且忽略吧。最后两个成员是对应inode的,正好df有个-i参数,不过也不常用。
前面四个成员正是对应常用df命令的,与结果对应的是:
- total = fsu_blocksize * fsu_blocks;
- used = fsu_blocksize * (fsu_blocks - fsu_bfree);
- available = fsu_blocksize * bavail;
- use% = used *100 / (used + available);
也就是说,计算百分比的那个分母是剔除了预留给root的空间的,而预留空间的计数方法是
- reserved = fsu_blocksize * (fsu_bfree - fsu_bavail);
收集这些数据的接口也定义在同一个头文件下,其原型是:
- int get_fs_usage ( char const *file, char const *disk, struct fs_usage *fsp);
这样一来,只要照抄df.c的相应代码,df能够显示的数据,我们也能够照样计算了,还能多计算一列预留空间。
列的算法知道了,下一步的问题是,如何像df那样,读出每行的数据,也就是获得每个文件系统的信息?