总结自书《Unix-Linux编程实践教程》
who命令
一、who命令能做什么
1.输入命令
显示用户名、终端名、登录时间等信息
**2.阅读手册 ** man who
NAME:命令名以及命令的简短说明
SYNOPSYS(概要):命令格式
DESCRIPTION:命令功能的详细阐述以及参数选项
二.who命令如何工作
1.阅读手册
了解到与utmp文件有关
wtmp与登录和注销有关,现在用不到
2.搜索联机帮助
●man -k utmp
了解到可能与utmp(5)有关
●man 5 utmp
了解到与utmp.h有关
●查找utmp.h
●查看utmp.h,发现utmp结构体中包含几个变量
ut_user保存登录名
ut_line保存设备名
ut_time保存时间
ut_host远程登录计算机名
●至此我们就知道了who的工作原理就是打开utmp文件并读取,然后关闭。
三、如何编写who命令
●需要做两件事:
1.从文件中读取数据
2.将数据合理的显示
为了完成第一件事,可以去搜索和读取文件有关的主题
man -k file|grep read
找到read命令并man 2 read 查看手册:
size_t read(int fd,void *buf,size_t count)
从fd(文件描述符)所指的文件传count个参数进入buf
类似的方法找到与打开关闭文件有关的open与close
fd open(char *name,int how)
int close(int fd)
完整代码
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
void show_info(struct utmp *utbufp);
void showtime(long);
int main()
{
struct utmp current_record;//read into
int utmpfd;//read from
int reclen=sizeof(current_record);
if((utmpfd=open(UTMP_FILE,O_RDONLY))==-1){
return -1;
}
while(read(utmpfd,¤t_record,reclen)==reclen)
show_info(¤t_record);
close(utmpfd);
return 0;
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)
//如果已经登陆,ut_type等于USER_PROCESS
return ;
printf("%-8.8s",utbufp->ut_name);//logname
//printf(" ");
printf("%-8.8s",utbufp->ut_line);//the tty
//printf(" ");
showtime(utbufp->ut_time);//调用showtime显示time_t类型
//printf(" ");
printf("(%s)",utbufp->ut_host);//the host
//printf(" ");
printf("\n"); //newline
}
void showtime(long timeval)
{
char *cp;
cp=ctime(&timeval);//ctime将秒数转换为日常使用的形式
printf("%12.12s",cp+4);//从第4个字符开始打印12个字符
}
四、如何提高效率
系统调用在内核中,内核和用户来回切换消耗很多资源,加入缓冲机制可以提高程序的效率,用一个能容纳多个utmp结构的数组作为缓冲区,编写utmp_next函数获取下一个utmp结构的数据
utmplib.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>//是Unix/Linux系统的基本系统数据类型的头文件
#include<utmp.h>
#include<unistd.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);
num_recs=0;
cur_rec=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;//读取的utmp数据个数
cur_rec=0;
return num_recs;
}
void utmp_close()
{
if(fd_utmp!=-1)
close(fd_utmp);
}
main.c
#include<stdio.h>
#include<fcntl.h>
#include<utmp.h>
#include<sys/types.h>
#include<time.h>
#include<stdlib.h>
#include<unistd.h>
void show_info(struct utmp *);
void showtime(time_t);
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;
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)
//如果已经登陆,ut_type等于USER_PROCESS
return ;
printf("%-8.8s",utbufp->ut_name);//logname
//printf(" ");
printf("%-8.8s",utbufp->ut_line);//the tty
//printf(" ");
showtime(utbufp->ut_time);//调用showtime显示time_t类型
//printf(" ");
printf("(%s)",utbufp->ut_host);//the host
//printf(" ");
printf("\n"); //newline
}
void showtime(long timeval)
{
char *cp;
cp=ctime(&timeval);//ctime将秒数转换为日常使用的形式
printf("%12.12s",cp+4);//从第4个字符开始打印12个字符
}