《Unix/Linux编程实践教程》着实有意思。
通常我们要做一件事情,会想着先储备足够的知识再来动手。但繁杂的知识往往成为一个问题,因此快速的学习能力显得尤为重要。说这本书有意思,是因为作者不是一上来就把知识点罗列出来,而是教我们如何一步步去获得我们需要的信息or知识。
下面我们看看怎样从零开始,编写一个简单的who命令!
#首先,实际使用一下~
$ who
#查看联机帮助
$ man who
#阅读联机帮助
$ man who
从联机帮助中我们可以知道,已登陆用户的信息是放在文件/var/adm/utmp中的,who通过读取该文件来获取信息。
另外,帮助里说,文件的格式是 utmp(4),那么我们循着这一步看一下:
$ man 4 utmp
(此外,我们可以通过 man -k utmp来搜索联机帮助,-k选项可以根据关键字来搜索联机帮助)
从联机文件里我们可以知道,utmp文件里保存的是结构数组,数组元素是utmp类型的结构,我们可以在utmp.h中找到utmp类型的定义!而utmp.h存放在/usr/include目录中。
所以下一步我们可以查看下utmp.h:
$ more /usr/include/utmp.h
通过联机文件,我们从一开始对who一无所知,到现在的初步了解,不得不说man真的很好用!
现在,我们可以猜测下who的工作原理:打开/var/adm/utmp文件,从文件中读取数据结构数组,然后显示出来!
我们需要做的两件事:
1.从文件中读取数据结构
2.将结构中的信息以合适的形式显示出来
问题1:如何从文件(file)读取(read)数据结构?
#搜索与file和read有关的主题
$ man -k file | grep read
我们可以找到一个可能的目标: read(2)
$ man 2 read
通过这个我们可以知道,该系统调用可以将文件中一定数目的字节读入一个缓冲区——为读取一个数据结构,我们可以用sizeof(struct utmp)来指定每次读入的字节数。另外,read打开的是文件描述符,那么如何获得文件描述符呢?
我们可以从联机帮助中的RELATED INFORMATION中看到一个open(2),也许这个是我们要找的,于是可以查看open的联机帮助,从中我们又可以找到close!
所以答案是:使用open,read,close。
接下来的工作就是读懂这3个系统调用的用法,然后付诸实践!
/* who1.c - a first version of the who program * open, read UTMP file, and show results */
#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#define SHOWHOST /* include remote machine on output */
int main()
{
struct utmp current_record; /* read info into here */
int utmpfd; /* read from this descriptor */
int reclen = sizeof(current_record);
if ( (utmpfd = open(UTMP_FILE, O_RDONLY)) == -1 ){
perror( UTMP_FILE ); /* UTMP_FILE is in utmp.h */
exit(1);
}
while ( read(utmpfd, ¤t_record, reclen) == reclen )
show_info(¤t_record);
close(utmpfd);
return 0; /* went ok */
}
/* * show info() * displays contents of the utmp struct in human readable form * *note* these sizes should not be hardwired */
show_info( struct utmp *utbufp )
{
printf("%-8.8s", utbufp->ut_name); /* the logname */
printf(" "); /* a space */
printf("%-8.8s", utbufp->ut_line); /* the tty */
printf(" "); /* a space */
printf("%10ld", utbufp->ut_time); /* login time */
printf(" "); /* a space */
#ifdef SHOWHOST
printf("(%s)", utbufp->ut_host); /* the host */
#endif
printf("\n"); /* newline */
}
编译运行,我们发现有一些问题:
存在空白的记录;
登陆时间格式显示有问题;
针对这两点的解决方法我就不在这里细说啦~
整体感受下来,思路很清晰,作者从初学者的角度,一点一点把我们想要的知识,信息纠出来,虽然很简单,但是跟随作者的节奏实践下来,真的觉得很爽!
在举了who的例子之后,作者又给了一个实现cp的例子,十分简单。这样子看来,好像UNIX编程挺简单的?
我们要明白,虽然功能是实现了,但是如果文件太大,那么在读,写的时候,岂不是要花费很多时间?
于是我们要再多问一个问题:如何使你的程序运行得更加有效率?
——答案是,使用缓冲。
为什么系统调用需要很多时间?
——因为系统调用在内核中,执行的时候需要从用户态切换到内核态,这个切换过程需要消耗大量时间。
在who1.c中,我们每次从utmp中读取一条记录,就像我们要煎3个蛋一样,去超市买一个,煎好,再买一个,再煎,再去买……
明智的做法应该是一次性买3个!
对应的,who程序中,我们可以一次性读取多个,比如16个utmp结构,然后显示,这样子效率就有所提高了。
缓冲技术在操作系统中应用很广泛,比如标准I/0等等~