在看了《Linux/UNIX系统编程手册》和APUE前几章之后发现缺少相应的实践,如果只是单纯的了解API并没有太大意义,因此决定跟随《UNIX/Linux编程实践教程》将书中的例子调通与理解透。然后再去啃APUE,效果应该会好很多。
书中第二章给出了who实现的,由于书写与2004年,代码看上去有些“另类”,对于编译过程中发现的问题进行修改,在讲解代码前先记录如下用法:
grep -r命令

 -r, --recursive
              Read all files  under  each  directory,  recursively,  following symbolic  links only if they are on the command line.  Note that if  no  file  operand  is  given,  grep  searches  the   working directory.  This is equivalent to the -d recurse option.

-R, --dereference-recursive
              Read  all  files  under each directory, recursively.  Follow all symbolic links, unlike -r.

例如如下命令 grep -n -R 'UTMP_FILE' /usr/include
是在/usr/include目录下递归查找字符串UTMP_FILE.
-R 与-r的区别是针对符号链接时有所不同。

#include <stdio.h>
#include <stdlib.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

void showtime( long );
void show_info( struct utmp * utbufp );
int main()
{
    struct utmp current_record;
    int     utmpfd;
    int     reclen = sizeof( current_record );

    if ( ( utmpfd = open( UTMP_FILE, O_RDONLY ) ) == -1 ){
        perror( UTMP_FILE );
        exit( 1 );
    }

    while ( read( utmpfd, &current_record, reclen ) == reclen )
        show_info( &current_record );
    close( utmpfd );
    return 0;
}

void show_info( struct utmp *utbufp )
{
    if ( utbufp->ut_type != USER_PROCESS )
        return;
    printf("%-8.8s ", utbufp->ut_name );
    printf("%-8.8s ", utbufp->ut_line );
    showtime( utbufp->ut_time );
#ifdef SHOWHOST
    if ( utbufp->ut_host[ 0 ] != '\0' )
        printf("(%s)", utbufp->ut_host);
#endif
    printf("\n");
}

void showtime( long timeval )
{
    char    *cp;
    cp = ctime( &timeval );

    printf("%12.12s", cp + 4 );
}

在上面的程序中UTMP_FILE通过grep查出来是_PATH_UTMP,使用grep用同样的方法可以知道其值为/var/run/utmp。
read:对于每个打开的文件系统内核会记录其文件偏移量,有时也将文件偏移量称为读写偏移量或指针。文件偏移量是指执行下一个read()或write()操作的文件或起始位置,会以相对于文件头部起始点的文件当前位置来表示。
ctime函数要输入一个指向time_t的指针,返回的时间字符串类似于以下格式:
Wed Jun 30 21:49:08 1993\n
由于who命令不需要指出星期几,所以在程序中输出从第4个字符开始输出。

运用缓冲技术

一次获取16条记录数据,用这种方法可以使read的调用次数减少到原来的1/16.

#include<stdio.h>
#include<stdlib.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
#define SHOWHOST /*include remote machine on output*/

#define NRECS 16
#define NULLUT ((struct utmp*) NULL)
#define UTSIZE (sizeof(struct utmp))

static char utmpbuf[NRECS* UTSIZE]; /*storage*/
static int  num_recs;               /*num stored*/
static int  cur_rec;                /*next to go */
static int  fd_utmp = -1;           /*read from*/

int utmp_open(char* filename)
{
    fd_utmp = open(filename,O_RDONLY);
    cur_rec = num_recs = 0;
    return fd_utmp;
}
int utmp_reload()
/*read next bunch of records into buffer*/
{
    int amt_read;
    amt_read = read(fd_utmp,utmpbuf,NRECS*UTSIZE);
    num_recs = amt_read/UTSIZE;
    cur_rec = 0;
    return num_recs;
}
struct utmp* utmp_next()
{
    struct utmp* recp;
    if(fd_utmp == -1)
        return NULLUT;
    if(cur_rec == num_recs && utmp_reload() == 0)
        return NULLUT;
    recp = (struct utmp* )&utmpbuf[cur_rec * UTSIZE];
    cur_rec ++;
    return recp;
}
void utmp_close()
{
    if(fd_utmp!=-1)
        close(fd_utmp);
}

void showtime(long timeval)  
{
    char* cp;
    cp = ctime(&timeval);
    printf("%12.12s",cp+4);
}
void show_info(struct utmp* utbufp)
{
    /*USER_PROCESS indicate the user is active or not*/
    if(utbufp->ut_type!= USER_PROCESS)
        return ;
    
    printf("%-8.8s ",utbufp->ut_name); /*this logname */
    printf("%-8.8s ",utbufp->ut_line); /*the logtty*/
    showtime(utbufp->ut_time);
    #ifdef SHOWHOST
        if(utbufp->ut_host[0] != '\0')
            printf("( %s )",utbufp->ut_host);  /*the host*/ 
    #endif
        printf("\n");
}

int main()
{
    struct utmp *utbufp; /*read info into here*/
    int    utmpfd;              /*read from the descriptor*/
    if(utmp_open(UTMP_FILE) == -1)
    {
        perror(UTMP_FILE);
        exit(1);
    }
    while((utbufp =utmp_next()) != ((struct utmp*)NULL))
    {
        show_info(utbufp);
    }
    utmp_close();
    return 0;
}