talin2010 发表于 2013-2-4 15:19:56

谁吃了你的硬盘空间?(1) 诡异的df算法

df命令,接触过Linux/Unix的人都用过吧?不过为什么有的数字总是算不对呢?

例如下面,/data那个文件系统,
total = 1404203532
used = 1203335028
available = 129539124
use% = 91%

但是,total - used = 200868504 <> available
而且,used / total = 86% <> use%

-bash-3.00$ dfFilesystem         1K-blocks      Used Available Use% Mounted on/dev/mapper/vg00-lvol00                     2031952    187976   174076010% //dev/sda2               497861   17285    454872   4% /boot/dev/mapper/vg00-lvol05                     1404203532 1203335028 12953912491% /datanone                   2074584         0   2074584   0% /dev/shm/dev/mapper/vg00-lvol04                      10095152   6955380   262695673% /home/dev/mapper/vg00-lvol02                      10095152   2091108   749122822% /usr/dev/mapper/vg00-lvol03                      10095152    229120   9353216   3% /varapams01-vip1:/mnt/mdraw1                     2097015808 1785570528 31144528086% /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那样,读出每行的数据,也就是获得每个文件系统的信息?
页: [1]
查看完整版本: 谁吃了你的硬盘空间?(1) 诡异的df算法