通过 strace 命令查看 df 主要使用了如下的系统调用:open、fstat、read、statfs
我这里实际上是模拟实现的 df --block-size=4096 这个命令,也就是说以 4096 字节为块大小来显示磁盘使用情况。
这里最为关键的是 statfs 这个结构体,该结构体的某些字段被用作 df 命令的输出字段:
struct statfs { long f_type; /* type of filesystem (see below) */ long f_bsize; /* optimal transfer block size */ long f_blocks; /* total data blocks in file system */ long f_bfree; /* free blocks in fs */ long f_bavail; /* free blocks avail to non-superuser */ long f_files; /* total file nodes in file system */ long f_ffree; /* free file nodes in fs */ fsid_t f_fsid; /* file system id */ long f_namelen; /* maximum length of filenames */ };比如:df --block-size=4096 的输出如下(纵向列出):
Filesystem /dev/sda1 4K-blocks 5077005 f_blocks 字段 Used 145105 f_blocks 字段 -f_bfree 字段 Available 4669841 f_bavail 字段 Use% 4% (f_blocks-f_bfree)/ f_blocks*100% 来计算磁盘使用率。 Mounted on /模拟实现的代码如下:
清单 5. 模拟实现代码
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/vfs.h> #include <math.h> #define SIZE1 100 #define FN "/etc/mtab" #define SPACE ' ' int displayapartition(char * pt,char * pt1); int main(void){ char tmpline[SIZE1]; FILE * fp; char * pt1; char * pt2; char * pt3; if( (fp = fopen(FN,"r")) == NULL ){ fprintf(stderr,"%s \n",strerror(errno)); exit(5); } while( fgets(tmpline, SIZE1, fp) != NULL ){ pt1=strchr(tmpline, SPACE); pt2=pt1+sizeof(char); *pt1='\0'; pt3=strchr(pt2,SPACE); *pt3='\0'; if(strstr(tmpline,"/dev") != NULL ){ displayapartition(tmpline,pt2); } } return 0; } int displayapartition(char * pt,char * pt1){ struct statfs buf; statfs(pt1,&buf); int usage; usage=ceil((buf.f_blocks-buf.f_bfree)*100/buf.f_blocks); printf("%s ",pt); printf("%ld ",buf.f_blocks); printf("%ld ",buf.f_blocks-buf.f_bfree); printf("%ld ",buf.f_bavail); printf("%d%% ",usage); printf("%s ",pt1); printf("\n"); return 0; }
下面解释一下这个程序:
首先,该程序定义了一个函数 displayapartition, 这里先定义它的函数原型。 然后我们从主程序说起:首先定义了一个 char tmpline[SIZE1] 数组,该数组用来存放从宏定义常量 FN 代表的文件中,打开后存入文件的每行记录。 接着定义了一个文件流指针和 3 个字符串指针。 接下来打开文件 FN 并把结果赋值给文件流变量 fp, 如果打开失败就退出。 下面从打开的文件流中读出 SIZE1 个字符到临时数组 tmpline。比如读出一行数据为:/dev/sda1 / ext3 rw 0 0 将把 /dev/sda1 放入数组 tmpline,把加载点 / 放入指针 pt2,同时判断字符串 tmpline 是否包含 /dev 字符串,这样来判断是否是一个磁盘文件,如果是的话就调用子函数 displayapartition,不是则返回。 子函数 displayapartition 是做什么的呢?该函数接受 2 个参数,一个是行 /dev/sda1 / ext3 rw 0 0 中的第一列比如:/dev/sda1 也就是实际磁盘作为 pt 指针,一个是行中的第二列比如:/ 也就是挂载点作为 pt1 指针。然后子函数通过 pt1 指针,读取挂载上的文件系统信息到 buf 数据结构里面。 根据开头介绍过的 statfs 结构体,buf.f_blocks 表示打开的文件系统的总数据块,buf.f_blocks-buf.f_bfree 表示已经使用的数据块,buf.f_bavail 表示非超级用户可用的剩余数据块,磁盘使用率就是前面列出过的计算表达式:(f_blocks- f_bfree)/ f_blocks*100%。通过子函数就可以打印出 df 需要显示的所有信息到标准输出了。