一.who的功能:
查看当前哪些用户正在使用系统
二.who的工作流程:
从 /var/run/utmp 文件中读取已登录用户的信息,然后输出
(注意:我的操作环境是ubuntu11.10 ,有些地方与其他系统不同)
如在我的ubuntu11.10中utmp.h在 /usr/include 和 /usr/include/i386-linux-gnu/bits 中
三.内容详解
/usr/include中的/utmp.h有:
#include <bits/utmp.h>
/* Compatibility names for the strings of the canonical file names. */
#define UTMP_FILE _PATH_UTMP
在path.h中有:
#define _PATH_UTMP "/var/run/utmp"
则 UTMP_FILE 就是文件路径/var/run/utmp
/usr/include/i386-linux-gnu/bits/utmp.h中包含信息:
#ifndef _UTMP_H
# error "Never include <bits/utmp.h> directly; use <utmp.h> instead."
#endif
#include <paths.h>
#include <sys/time.h>
#include <sys/types.h>
#include <bits/wordsize.h>
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
/* The structure describing an entry in the database of
previous logins. */
struct lastlog
{
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ll_time;
#else
__time_t ll_time;
#endif
char ll_line[UT_LINESIZE];
char ll_host[UT_HOSTSIZE];
};
/* The structure describing the status of a terminated process. This
type is used in `struct utmp' below. */
struct exit_status
{
short int e_termination; /* Process termination status. */
short int e_exit; /* Process exit status. */
};
/* The structure describing an entry in the user accounting database. */
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]; /* Devicename. */
char ut_id[4]; /* Inittab ID. */
char ut_user[UT_NAMESIZE]; /* Username. */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
/* 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, used for windowing. */
struct
{
int32_t tv_sec; /* Seconds. */
int32_t tv_usec; /* Microseconds. */
} ut_tv; /* Time entry was made. */
#else
long int ut_session; /* Session ID, used for windowing. */
struct timeval ut_tv; /* Time entry was made. */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote host. */
char __unused[20]; /* Reserved for future use. */
};
/* Backwards compatibility hacks. */
#define ut_name ut_user
#ifndef _NO_UT_TIME
/* We have a problem here: `ut_time' is also used otherwise. Define
_NO_UT_TIME if the compiler complains. */
# define ut_time ut_tv.tv_sec
#endif
#define ut_xtime ut_tv.tv_sec
#define ut_addr ut_addr_v6[0]
/* Values for the `ut_type' field of a `struct utmp'. */
#define EMPTY 0 /* No valid user accounting information. */
#define RUN_LVL 1 /* The system's runlevel. */
#define BOOT_TIME 2 /* Time of system boot. */
#define NEW_TIME 3 /* Time after system clock changed. */
#define OLD_TIME 4 /* Time when system clock changed. */
#define INIT_PROCESS 5 /* Process spawned by the init process. */
#define LOGIN_PROCESS 6 /* Session leader of a logged in user. */
#define USER_PROCESS 7 /* Normal process. */
#define DEAD_PROCESS 8 /* Terminated process. */
#define ACCOUNTING 9
/* Old Linux name for the EMPTY type. */
#define UT_UNKNOWN EMPTY
/* Tell the user that we have a modern system with UT_HOST, UT_PID,
UT_TYPE, UT_ID and UT_TV fields. */
#define _HAVE_UT_TYPE 1
#define _HAVE_UT_PID 1
#define _HAVE_UT_ID 1
#define _HAVE_UT_TV 1
#define _HAVE_UT_HOST 1
struct utmp结构保存登录信息
四.自己写的who命令的代码(版本一):
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
#define SHOWHOST
void showtime(long);
void show_info(struct utmp *utbufp);
int main()
{
struct utmp utbuf; // read info into here
int utmpfd; // read from this descriptor
if( (utmpfd = open(UTMP_FILE,O_RDONLY)) == -1)
{
perror(UTMP_FILE); //UTMP_FILE is in utmp.h
exit(1);
}
while( read( utmpfd,&utbuf,sizeof(utbuf)) ==sizeof(utbuf))
show_info(&utbuf);
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_user);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime( utbufp->ut_time);
// printf("%12.12s",ctime(&(utbufp->ut_tv).tv_sec)+4);
printf(" ");
#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);
}
五.运用缓冲技术的who命令代码(版本二):
在版本一中,每次只能读取一个数据,因此需要不停的读取,所以导致效率低下。可以加入缓冲机制提高程序的运行效率。缓冲技术的主要思想是一次读入大量的数据放入缓冲区,需要的时候从缓冲区取得数据。
程序使用一个 utmplib.c文件实现缓冲算法,在main函数中只要调用 utmplib.c中相应函数即可。在 utmplib.c中用一个能容纳16个utmp结构的数组作为缓冲区。utmp_next 函数来从缓冲区取得下一个utmp结构的数据。
修改原来的main 函数,通过调用utmp_next 来取得数据,当缓冲区的数据都被取出后,utmp_next会调用read,通过内核再次获得16条记录充满缓冲区,用这种方法可以是read的调用次数减少到原来的 1/16 。
utmplib.c 代码如下:
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<sys/types.h>
#define NRECS 16
#define NULLUT ((struct utmp *) NULL )
#define UTSIZE (sizeof(struct utmp))
static char utmpbuf[NRECS * UTSIZE]; //一次可以存储16个utmp结构的数组
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;
}
/*
* utmp_reload返回值是缓冲区的数据个数
*/
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 ;
}
/*
* utmp_next 返回指向结构的指针
*/
struct utmp *utmp_next()
{
struct utmp *recp;
if( fd_utmp == -1)
return NULLUT;
if( cur_rec == num_recs && utmp_reload() == 0)
return NULLUT;
// recp指向下一个数据
recp = (struct utmp *)&utmpbuf[cur_rec * UTSIZE];
cur_rec++;
return recp;
}
void utmp_close()
{
if(fd_utmp != -1)
close(fd_utmp);
}
who.c中只更改main函数,其余不变,main函数代码如下:
int main()
{
struct utmp *utbufp,
*utmp_next();
if( (utmp_open(UTMP_FILE)) == -1)
{
perror(UTMP_FILE); //UTMP_FILE is in utmp.h
exit(1);
}
while(( utbufp = utmp_next()) != ((struct utmp *) NULL))
show_info(utbufp);
utmp_close();
return 0;
}