Linux中who命令(涉及缓冲来实现who)

本篇笔记的所有操作是在VMware12.5.2,Ubuntu14.04.1下进行的。如果由大神对我写的文章有兴趣的话,文章后面有我的两个问题,希望大神不吝赐教!

  • who命令工作流程
    1.打开utmp文件
    2.读取utmp文件里的内容,并保存在struct utmp结构体中
    3.将结构体中的内容展示出来

  • strcut 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命令时没有将所有的struct utmp的成员都用到的。

  • 要使用的函数原型介绍
    int fd=open(char *filename,int how);
    ssize_t numread=read(int fd,void *buf,size_t qty);
    int result=close(int fd);

  • 内核在who命令中的作用(这个是需要重点理解的)
    1.在打开utmp文件的时候,我们使用的是open函数。注意,这个不是c标准库里的函数,是Linux系统函数,移植性不高。open函数是系统的调用,之前我们大多数写的程序都是进程的调用。例如:在你的test.c文件里,有一个functionA(),functionB(),都在主函数里被执行,此时,functionA(),functionB()就是都是进程在调用;了解过C++,java的人都知道,在他们中函数的调用是需要对象来完成的,但是c语言是面向过程的语言,是没有对象的。所以,一个函数是怎么就启动了捏?当然,就是test.c所形成的test.exe文件的这个进程所调用的了。回到主题,open()函数不是进程所调用,而是内核的调用,所以内核的作用就体现出来了。
    2.在open一个文件后,自然就得去read它,不然打开就显得意义不大了。这里要强调的是open函数的返回值是一个文件描述符fd,不是c标准函数fopen的一个文件指针FILE *,其中的区别我就在这里细说了。而read()函数也恰恰需要这样一个文件指针。在unix/Linux编程实践教程一书上面对于文件描述符是这样说的:它就像一条由进程通向内核的管道

  • who01.c代码

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

void show_info(struct utmp* utbufp);
void showtime(long timeval);
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(" ");
 printf("%-8.8s",utbufp->ut_line);
 printf(" ");
 showtime(utbufp->ut_time);
 printf("\n");
}

void showtime(long timeval)
{
 char *cp;
 cp=ctime(&timeval);
 printf("%12.12s",cp+4);
}

  • 改进who01.c的方法—使用缓冲
    1.为什么执行read方法耗时长?用户进程位于用户空间,内核位于系统空间,而磁盘只能被内核直接访问,并且文件的数据是存储在磁盘上面的。程序要读取磁盘上文件的数据就只能通过系统调用read()方法,而read方法的代码是在内核中,所以当read被调用的时候,执行权会从用户代码到内核代码,执行切换是需要时间的。
    2.为了减少切换所需要的时间,所以我们使用一个缓冲数组,一次性读取20sizeof(struct utmp)字节的数据(当然,也不一定就是20个,只是比喻一下),然后,以后取数据就从缓冲数组里取出,直到缓冲数组的数据读取完毕了,就再调用read方法,从文件里将数据读取出来到缓冲数组里。下面给出who02.c的代码

  • who02.c代码

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<utmp.h>
#include<fcntl.h>
#include<time.h>
#include"utmplib.c"

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);
 }
 if(utbufp==NULL)
  printf("指针为空\n");
 utmp_close();
 return 0;
}


void show_info(struct utmp* utbufp)
{
 //printf("执行show_info\n");
 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("\n");
}

void showtime(time_t timeval)
{
 char *cp;
 cp=ctime(&timeval);
 printf("%12.12s",cp+4);
}
//这里给出的是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))

typedef struct utmp utmp;
//static  utmp utmpbuf[NRECS*UTSIZE];
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];
 //recp=&utmpbuf[cur_rec*UTSIZE];
 cur_rec++;
 return recp;
}

int utmp_reload()
{
 int amt_read;
 amt_read=read(fd_utmp,utmpbuf,NRECS*UTSIZE);
 printf("执行reload函数\n");
 num_recs=amt_read/UTSIZE;
 cur_rec=0;
 return num_recs;
}

void utmp_close()
{
 if(fd_utmp!=-1)
   close(fd_utmp);
}
  • 总结
    1.这篇笔记主要就是想要说一个关于内核在who命令中的作用。
    2.还有两个问题没有解决,如果有那个网友知道原因的话,欢迎私信我,或者下面留言,我们互相讨论。
    问题一:在书本上使用的是系统自带的函数open,read之类的,我想问一下为什么不使用fopen,fread之类的函数。经过我的测试,我自己写了一个使用fopen函数来实现who命令的函数,也是可以使用的,所以我不清楚为什么书本上一定要使用open函数,还是说书本上就是为了引出缓冲的概念,从而使用的open之类的函数。
    问题二:在使用缓冲区的时候,为什么使用的缓冲数组是char型,而不是struct utmp型。按照我的想法,我们是使用缓冲数组来存贮在utmp文件中的数据(并且一次读取n个sizeof(strcut utmp)),那使用struct utmp型数组来“装”不是情理之中的事情吗?为此,我在windows平台测试了一下,发现在windows下的devc++编译器是会报错了,因为
    recp=(struct utmp*)&utmpbuf[cur_recUTSIZE];会报错,但是在Ubuntu下不会报错,并且如果你写成我在utmplib.c文件注释的那个样子,就得不到正确的结果了。(不同编译环境下会报错这个可以理解,但是为什么我把缓冲区写成static utmp utmpbuf[NRECSUTSIZE];就得不到正确的结果捏)。
    3.这篇笔记是我作为一个Linux新手写的,可能会有很多纰漏之处,希望大神们不要嫌弃,还希望大神多多指教!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值