让我们来运行一下该程序:
程序的运行情况如下,假设编译后的可执行文件名为 emulatetac,有一个文本文件 test.txt。
| # gcc emulatetac.c -o emulatetac # cat test.txt 1 2 3 a b # ./emulatetac test.txt b a 3 2 1 |
可以看出文件内容以倒序方式显示输出了。
下面逐行讲解:
- #include 的头文件,都应该通过 man 2 系统调用命令来查找,这里就不多说了。
- 下面定义了一个宏常量 SIZE,该常量主要用来表示能够读入最大多少个字节的文件,当文件过大的时候程序就不执行,直接退出。然后定义了宏常量 NLINE 表示换行符'\n'。
- 接下来主程序体开始了:首先定义一个字符数组 buf,用来把读入文件的每个字节都存在该数组里面。
- 然后定义了 4 个字符串指针,一个指向结构体 struct stat 的指针 fp,一个文件描述符。
- 然后为指向结构体的指针 fp 分配存储空间。
- 接下来判断输入参数是否为 2 个,也就是命令本身和文件名。不是 2 个就直接退出。
- 然后以只读方式打开输入文件名的文件,也就是 test.txt。打开成功的话,把打开的文件赋值到文件描述符 fd 中,错误的话退出。
- 然后用 fstat 系统调用把文件描述符 fd 中对应文件的元信息,存放到结构体指针 fp 指向的结构中。
- 下面判断当文件的大小超过缓冲区数组 buf 的大小 SIZE-1 时,就退出。
- 下面将把文件 test.txt 中的每个字符存放到数组 buf 中。
- 下面是程序的核心部分:首先我们找到字符串 buf 中的第一个换行字符存放到 p1 指针里面,然后把最后一个换行字符置为字符串结束符。
- 接下来我们从后往前查找字符串 buf 中的换行符,直到遇到第一个换行符 p1。同时打印每个找到的换行符'\n'中的下一个字符开始的字符串,也就刚好是一行文本。
- 最后当从后向前找到第一个换行字符时,打印第一行,程序结束。
通过 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 |