1、who能做什么
who命令可以显示出当前系统中已经登录的用户信息,输入who命令,就可以知道有谁正在使用系统。
who命令每一行代表一个已经登录的用户,第1列是用户名,第2列是终端名,第3列是登录时间,第4列是用户的登录地址。
2、who是如何实现的
在联机帮助中描述了who的功能和用法,所以通过联机帮助可以学习who任何实现与工作的。
- 阅读联机帮助:man who,在描述部分写了已登录用户的信息是放在/var/adm/utmp文件中,who就是通过读该文件获得信息。
- 搜索联机帮助:man -k utmp,然后通过搜索联机帮助来了解/var/adm/utmp文件的结构信息
- 阅读.h文件:搜索到/var/adm/utmp文件,然后通过命令man 5 utmp查看该帮助内容,就可以看到utmp.h文件内容,可以看到utmp结构保存了登录记录,ut_user保存登录名,ut_line保存设备名也就是用户终端类型,ut_time保存登录时间,ut_host保存用户用于登录的远程计算机的名字。
open、read和close系统调用:
- open():打开一个文件,fd=open(char *name,int how),打开文件name,以模式how方式打开,how的三种打开模式:O_RDONLY、O_WRONLY、O_RDWR,最后返回文件描述符fd。
- read():把数据读取到缓冲区,numread=read(int fd,void *buf,size_t qty),从fd所指定的文件中读取qty字节数据,存放在buf所指定的内存空间中,如果成功返回读取数据的字节数numread。
- close():关闭一个文件,result=close(int fd),关闭进程和文件fd直接的连接。
who的工作原理:通过阅读who和utmp的联机帮助,以及头文件utmp.h,who通过读文件来获得需要的信息,而每个登录的用户在文件中都有对应的记录。who的流程打开utmp、读取记录、显示记录、关闭utmp。
struct utmp {
short ut_type; /* Type of record */
pid_t ut_pid; /* PID of login process */
char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
char ut_id[4]; /* Terminal name suffix,
or inittab(5) ID */
char ut_user[UT_NAMESIZE]; /* Username */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernel version for run-level
messages */
struct exit_status ut_exit; /* Exit status of a process
marked as DEAD_PROCESS; not
used by Linux init(8) */
/* The ut_session and ut_tv fields must be the same size when
compiled 32- and 64-bit. This allows data files and shared
memory to be shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ut_session; /* Session ID (getsid(2)),
used for windowing */
struct {
int32_t tv_sec; /* Seconds */
int32_t tv_usec; /* Microseconds */
} ut_tv; /* Time entry was made */
#else
long ut_session; /* Session ID */
struct timeval ut_tv; /* Time entry was made */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote
host; IPv4 address uses
just ut_addr_v6[0] */
char __unused[20]; /* Reserved for future use */
};
3、自己编写一个who
who使用了open、read、close三个系统调用,open这个系统调用在进程和文件之间建立一条连接,这个连接称为文件描述符,是用来唯一标识这个连接的,它像一条由进程通向内核的管道,然后内核提供打开文件服务。
who1.c
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#define SHOWHOST
void show_info(struct utmp *utbufp)
{
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
printf("%10ld",utbufp->ut_time);
printf(" ");
#ifdef SHOWHOST
printf("(%s)",utbufp->ut_host);
#endif
printf("\n");
}
int main()
{
struct utmp current_record;
int fd;
int recordlen=sizeof(current_record);
if((fd=open(UTMP_FILE,O_RDONLY))==-1)
{
perror(UTMP_FILE);
exit(1);
}
while(read(fd,¤t_record,recordlen)==recordlen)
show_info(¤t_record);
close(fd);
return 0;
}
who2.c
改进:清除空白记录和正确显示登录时间
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
#define SHOWHOST
void showtime(long timeval)
{
char *cp;
cp=ctime(&timeval);
printf("%12.12s",cp+4);
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)
return;
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime(utbufp->ut_time);
printf(" ");
#ifdef SHOWHOST
printf("(%s)",utbufp->ut_host);
#endif
printf("\n");
}
int main()
{
struct utmp current_record;
int fd;
int recordlen=sizeof(current_record);
if((fd=open(UTMP_FILE,O_RDONLY))==-1)
{
perror(UTMP_FILE);
exit(1);
}
while(read(fd,¤t_record,recordlen)==recordlen)
show_info(¤t_record);
close(fd);
return 0;
}
who3.c
在who中运用缓冲技术,用utmplib来缓冲main函数调用utmplib.c中的函数来取得下一条utmp记录,utmplib.c中定义的函数每次从文件中读取16条记录放入数组中,当数组中16条记录都被取走后,才调用内核服务重新读取数据。
用一个能容纳16个utmp结构的数组作为缓冲区,编写utmp_next函数来从缓冲区中取得下一个utmp结构的数据,通过调用utmp_next来取得数据,当缓冲区的数据都被取走后,utmp_next会调用read,通过内核再次获得16条记录充满缓冲区。用这种方法可以使read的调用次数减少到原来的1/16。
utmplib.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<utmp.h>
#define NRECS 16
#define NULLUT ((struct utmp*)NULL)
#define UTSIZE (sizeof(struct utmp))
static char utmpbuf[NRECS*UTSIZE];
static int num_recs;
static int cur_rec;
static int fd_utmp=-1;
int utmp_open(char *filename)
{
fd_utmp=open(filename,O_RDONLY);
cur_rec=num_recs=0;
return fd_utmp;
}
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;
}
int utmp_reload()
{
int amt_read;
amt_read=read(fd_utmp,utmpbuf,NRECS*UTSIZE);
num_recs=amt_read/UTSIZE;
cur_rec=0;
return num_recs;
}
void utmp_close()
{
if(fd_utmp!=-1)
close(fd_utmp);
}
who3.c
#include<stdio.h>
#include<utmp.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
#include "utmplib.c"
#define SHOWHOST
void showtime(long timeval)
{
char *cp;
cp=ctime(&timeval);
printf("%12.12s",cp+4);
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)
return;
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime(utbufp->ut_time);
printf(" ");
#ifdef SHOWHOST
printf("(%s)",utbufp->ut_host);
#endif
printf("\n");
}
int main()
{
struct utmp *utbufp;
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;
}