写linux命令程序who的几点总结

最近在一边学习C一边看《Unix-Linux编程实践教程》。这本书在去年就已经看过一遍了,其中的例子当时也跟着编过。本来是最近是准备看《UNIX环境高级编程》这本书的,突然发现《Unix-Linux编程实践教程》的内容都已忘的差不多了,就决定先再把它看一遍。

既然要拿起来在学,就好好学一下吧。决定自己先不看书,自己动手先再重写一遍。根据书中的套路(这个现在居然还记得):1、这个程序能干嘛?2、这个程序是如何实现的?3、自己动手写一个。在这几个过程中终于是体会到在linux底下写程序的方便了,主要是依靠man。

程序涉及到的主要有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 */
           };
可以看到who命令所需要展示的几乎所有信息在这个结构体中都有。那这个结构体中的数据又要去哪里找呢?继续查看utmp.h 文件,发现有下面一个宏定义:

#define UTMP_FILE _PATH_UTMP
继续用grep命令查找发现_PATH_UTMP 是在这里定义的

/usr/include/paths.h:64:#define _PATH_UTMP "/var/run/utmp"
到现在应该是可以肯定utmp结构体书储存在/var/run/utmp 这个文件中的了。

为了从文件中读取数据,就需要了解几个同文件读取有关的函数了。我们需要的操作应该是打开UTMP_FILE,读取其中的数据,最后关闭这个文件。作为一个C语言初学者,一开始想到的可能是fopen、fscanf和fclose这三个函数。但是fscanf无法读取一个数据结构的数据。很自然的就会用到open、read和close这几个系统调用了。下面是这几个函数的基本信息:

int open(const char *pathname, int flags);
ssize_t read(int fd, void *buf, size_t count);
int close(int fd);
其中read是根据字节数来读取文件中内容的,我们当然可以获取一个结构体的大小。有了以上的这些内容就可以开始写who程序了:

#include 
#include 
#include 
#include 
#include 
#define UTMP_SIZE (sizeof(struct utmp))
void inline disUInfo(struct utmp uInfo)
{
time_t tmpTime;
struct tm *newTm;
tmpTime = uInfo.ut_time;
newTm = localtime(&tmpTime);
printf("%10.10s ", uInfo.ut_user);
printf("%10.10s ", uInfo.ut_line);
printf("%4d-%02d-%02d %02d:%02d ", newTm->tm_year + 1900,
newTm->tm_mon + 1,
newTm->tm_mday,
newTm->tm_hour,
newTm->tm_min);
// printf("%20.20s ", ctime(&tmpTime));
printf("[%20.20s]", uInfo.ut_host);
printf("\n");
}
int main(int argc, char *argv[])
{
int fd; //file discriptor
/*
* you can just define a struct but not a pointer
*/
struct utmp *uInfo = malloc(UTMP_SIZE);
fd = open(UTMP_FILE, O_RDONLY);
if (-1 == fd) {
perror(UTMP_FILE);
exit(EXIT_SUCCESS);
}
while (UTMP_SIZE == read(fd, uInfo, UTMP_SIZE)) {
if (uInfo->ut_type == USER_PROCESS) { // Normal process
disUInfo(*uInfo);//trans value do not trans the pointer
}
}
close(fd); //don't forget close the fd
free(uInfo);//don't forget to free the memory
return 0;
}

将这个程序和之前我写的程序进行比较时有两点是需要总结的:

1、在之前写的程序中指向结构体utmp的指针uinfo我没有初始化就直接使用了。OMG,程序跑起来当然是没什么问题的,但是肯定是修改系统中其他地方的内存了(跑了这么多次系统居然没崩溃…)。这就是C语言不强制要求指针使用之前必须初始化,并且自己不小心的结果吧。下面只将uinfo指针的值初始化为null,看运行程序有什么结果。

struct utmp *uinfo = NULL //只将上面的程序修改这一行
如果这么改的话main函数一进去就退出了,也没报错感觉到有点奇怪。这个问题暂且记下以后找个时间专门研究一下。

2、在之前的程序中我写了一个函数,现在看能没有任何作用。现在我也看不出有什么作用了,主要还是因为没写好注释的缘故。以后再写程序不能在忽视注释了。

在写这个程序的过程中有几点需要总结的:

1、在写的过程中居然不大确定printf格式符 %m.nX 中m、n的含义了。在这里在写一遍:a、对于基本类型:m指定了要显示的最少字符数。如果要显示的数值所需的字符数少于m,那么值在显示宽度内是右对齐的。如果要显示的值所需的字符数多于m,那么显示宽度会自动扩展为所需的尺寸。n表示小数点后的位数。b、m的含义同基本类型相同,n表示只显示字符串的前n个字符。

2、关于时间的显示转换,我最开始用的是ctime、后来用gmtime最后在用localtime的。ctime的返回值是一个字符串,其格式是:"Wed Jun 30 21:49:08 1993\n";gmtime和localtime的返回值都是一个tm结构体(可以去查man)。不同的是localtime的tm_hour会加上你所在的时区。这两个函数的年份你要加上1900,月份是从0开始算的要正常显示的话得加上1.

你可能感兴趣的:(Linux)