最近阅读UULP(Understanding Unix/Linux Programming),按照书中介绍对Unix/Linux系统编程进行学习梳理,总结如下。

1. who命令能做什么

who命令用于查看有谁在使用系统。

执行who命令。

who

其输出格式如下:

ustc   tty7    Sept  1   08:34  (xxx.yy.com用户登陆地址,有些版本没有)

john   lft/0   Sept  1   08:34 

其显示包含用户名,终端名,和登录时间。

2. who命令是如何工作的

想知道如何自己编写这样一个命令,首先当然是要了解who是如何工作的?最好的方法自然是先查看一下联机帮助(man)。

man who

查看输出结果,在描述部分有这样一句话:

  If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common.

所以who的实现,跟utmp/wtmp文件应该有很大的联系。

继续对utmp查看联机帮助。

man utmp

果然在描述部分里有:

  The utmp file allows one to discover information about who is currently using the system.

表明的确可以通过utmp查看谁在使用系统。

进一步查看其数据结构,发现与我们who命令需要相关的用户名,终端名,和登录时间。

 char    ut_user[UT_NAMESIZE]; /* Username */
char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernel version for run-level
struct {
int32_t tv_sec; /* Seconds */
int32_t tv_usec; /* Microseconds */
} ut_tv; /* Time entry was made */

他们都在struct utmp中。

3. 编写自己的who命令

3.1 who1.c

有了第二部分的了解之后,编写自己的who命令看起来就有眉目了,我们只需要利用unix文件操作,读取utmp文件中的相应项,并以固定格式输出在屏幕即可。

基础的文件操作函数接口复习一下:

#include <fcntl.h>
int open( char *filename, int access, int permission );
int read( int handle, void *buffer, int nbyte );
int write( int handle, void *buffer, int nbyte );
int close( int handle );

所以对相关项以固定格式输出,利用#SHOWHOST控制是否显示第四项(用户登录地址),得到第一个版本的who命令.

who1.c

代码:

 #include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#define SHOWHOST void show_info(struct utmp* utbufp) {
printf("%8.8s", utbufp -> ut_user);
printf(" ");
printf("%8.8s", utbufp -> ut_line);
printf(" ");
printf("%d", utbufp -> ut_tv.tv_sec);
printf(" ");
#ifdef SHOWHOST
printf("%s", utbufp -> ut_host);
#endif
printf("\n");
} int main() {
struct utmp current_record;
int utmpfd;
int reclen = sizeof(current_record);
if ((utmpfd = open(UTMP_FILE,O_RDONLY)) == -) {
perror(UTMP_FILE);
exit();
}
while (read(utmpfd,&current_record,reclen) == reclen) {
show_info(&current_record);
}
close(utmpfd);
return ;
}

查看一下输出:

reboot     ~     1472787188 4.4.0-34-generic
runlevel   ~      1472787197 4.4.0-34-generic
LOGIN     tty1  1472787200
ustc        tty7  1472787242 :0

对比系统who命令输出,多了一些项,而且时间显示好像也不是可读的时间,所以需要进一步修改。

3.2 who2.c

who1.c的输出中包含了所有终端的信息,甚至是尚未使用的,同时还有LOGIN这样的控制台信息(不是真正的用户)。

所以考虑如何去掉他们,继续查看utmp的联机帮助,在关于ut_type中有这样的定义。

#define EMPTY         0 /* Record does not contain valid info
(formerly known as UT_UNKNOWN on Linux) */
#define RUN_LVL 1 /* Change in system run-level (see
init()) */
#define BOOT_TIME 2 /* Time of system boot (in ut_tv) */
#define NEW_TIME 3 /* Time after system clock change
(in ut_tv) */
#define OLD_TIME 4 /* Time before system clock change
(in ut_tv) */
#define INIT_PROCESS 5 /* Process spawned by init(8) */
#define LOGIN_PROCESS 6 /* Session leader process for user login */
#define USER_PROCESS 7 /* Normal process */

可以看出,ut_type的值为7时,表明其是一个user_process,所以加一句判断,即可消除其他行。

代码:

if (utbufp -> ut_type != USER_PROCESS) {
return;
}

再看时间显示问题,unix有库函数ctime可以将time_t类型转换为人常用的时间形式。

查看ctime的man文档,学习其使用方式:

char *ctime(const time_t *timep);

发现给定一个time_t类型(long int),即可转换为一个可读字符串,还是很简单的。

注: 如果不知道ctime,也可以通过

man time | grep transform之类的命令,查找内容中带有转换字样的与时间相关的命令,在显示结果中找到可能有用的,再细看其man文档,发现ctime。

这样,把上述两部分总结起来,就可以得到第二个版本的who。

who2.c

 #include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
#define SHOWHOST
void show_time(long timeval) {
char* cp;
cp = ctime(&timeval);
printf("%12.12s",cp + );
} void show_info(struct utmp* utbufp) {
if (utbufp -> ut_type != USER_PROCESS) {
return;
}
printf("%-8.8s", utbufp -> ut_user);
printf(" ");
printf("%-8.8s", utbufp -> ut_line);
printf(" ");
show_time(utbufp -> ut_tv.tv_sec);
printf(" ");
#ifdef SHOWHOST
printf("%s", utbufp -> ut_host);
#endif
printf("\n");
} int main() {
struct utmp current_record;
int utmpfd;
int reclen = sizeof(current_record);
if ((utmpfd = open(UTMP_FILE,O_RDONLY)) == -) {
perror(UTMP_FILE);
exit();
}
while (read(utmpfd,&current_record,reclen) == reclen) {
show_info(&current_record);
}
close(utmpfd);
return ;
}

查看其输出结果:

ustc   tty7   Sep  1  20:34 :0

与系统自带的who没什么区别了。至此,一个能用的who命令应该是完成了。

3.3. 继续优化,who3.c

一个能用的who命令是编写好了,但是其有没有不足之处呢?显然是有的,一个最大的问题,就是文件的I/O效率太低了。

因为每次读写操作的系统调用CPU都要完成用户态与内核态之间的切换,假设结果中有大量用户信息要去读,则大量的资源消耗在了这里,所以这是优化的重点。

而优化的思路也很简单,就是使用缓冲区。每次从内核多读一些进来进缓冲区,然后从缓冲区读取utmp数据进行显示。

把缓冲区大小设置为16,开闭读取文件都用写好的接口从缓冲区读取和写入数据,有如下代码:

 //utmplib.h
#ifndef UTMPLIB_H
#define UTMPLIB_H #define NRECS 16
#define NULLUT (struct utmp *)NULL
#define UTSIZE (sizeof (struct utmp)) static char utmpbuf[NRECS * UTSIZE]; //storage
static int num_recs; //nums of stored utmp data
static int cur_rec; //next pos to go
static int fd_utmp = -; //read from int utmp_open(char* filename); struct utmp* utmp_next(); int utmp_reload(); void utmp_close(); #endif
 //utmplib.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<utmp.h>
#include"utmplib.h" int utmp_open(char* filename) {
fd_utmp = open(filename, O_RDONLY);
cur_rec = num_recs = ;
return fd_utmp;
} struct utmp* utmp_next() {
struct utmp* recp;
if (fd_utmp == -) {
return NULLUT;
}
if (cur_rec == num_recs && utmp_reload() == ) {
return NULLUT;
}
recp = (struct utmp*) &utmpbuf[cur_rec * UTSIZE];
cur_rec ++;
return recp;
} int utmp_reload() {
int aimt_read;
aimt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE);
num_recs = aimt_read / UTSIZE;
cur_rec = ;
return num_recs;
} void utmp_close() {
if (fd_utmp != -) {
close(fd_utmp);
}
return;
}
 //who3.c
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
#include"utmplib.h"
#define SHOWHOST
void show_time(long timeval) {
char* cp;
cp = ctime(&timeval);
printf("%12.12s",cp + );
} void show_info(struct utmp* utbufp) {
if (utbufp -> ut_type != USER_PROCESS) {
return;
}
printf("%-8.8s", utbufp -> ut_user);
printf(" ");
printf("%-8.8s", utbufp -> ut_line);
printf(" ");
show_time(utbufp -> ut_tv.tv_sec);
printf(" ");
#ifdef SHOWHOST
printf("%s", utbufp -> ut_host);
#endif
printf("\n");
} int main() {
struct utmp* utbufp;
if (utmp_open(UTMP_FILE) == -) {
perror(UTMP_FILE);
exit();
}
while ((utbufp = utmp_next()) != (struct utmp*)NULL) {
show_info(utbufp);
}
utmp_close();
return ;
}

至此一个who命令基本编写完毕。

我们在这个过程中大量使用了man文档学习和辅助编写,复习了基本的文件操作函数,学习了使用缓冲技术提高I/O效率。

参考文献:

Bruce Molay, Understanding Unix/Linux programming - A Guide to Theory and Practice.

代码也可以通过https://github.com/wangxiaobao1114/Unix-Linux_Programming查看。

编写who命令:文件操作,缓冲区与联机帮助的更多相关文章

  1. Linux常用命令--文件操作

    常用Linux命令笔记(1) 1. 创建文件/文件夹 参考博客:https://www.cnblogs.com/lclq/p/5741852.html. 使用cat命令创建新文件: 输入命令 # ca ...

  2. Linux常用命令--文件操作、权限设置

    1.编辑文件 cat aaa.txt 查看aaa.txt文件的内容 head - aaa.txt 查看aaa.txt文件前5行的内容 tail - aaa.txt 展示aaa.txt文件最后10行的内 ...

  3. Linux之基础命令——文件操作

    ls(显示指定工作目录下的内容) -a 显示所有文件及目录 包括隐藏文件 -l 除文件名称外,还会将文件类型.权限.拥有者.文件大小等信息详细列出[可以ll简写] -r 将文件以相反次序显示(默认是a ...

  4. linux常用命令之------文件操作、文件查看、权限、打包压缩

    1.一般公司把linux作为自己的应用服务器,将应用和服务器部署在上面 2.测试一般用来打包.压缩.查日志,写一个简单的shell 获得linux服务器的方式 a:网上租一台云服务器 b:安装vmwa ...

  5. sed命令针对文件操作具体解释

    Linux的简单shell脚本中改动文件操作 1.Sed简单介绍 sed 是一种在线编辑器,它一次处理一行内容.处理时.把当前处理的行存储在暂时缓冲区中,称为"模式空间"(patt ...

  6. dos命令:文件操作

    文件操作 一.assoc命令 1.介绍 显示或修改文件扩展名关联 2.语法 ASSOC [.ext[=[fileType]]] .ext     指定跟文件类型关联的文件扩展名 fileType 指定 ...

  7. go语言之行--文件操作、命令行参数、序列化与反序列化详解

    一.简介 文件操作对于我们来说也是非常常用的,在python中使用open函数来对文件进行操作,而在go语言中我们使用os.File对文件进行操作. 二.终端读写 操作终端句柄常量 os.Stdin: ...

  8. Linux命令-文件文本操作grep

    文件文本操作 grep 在文件中查找符合正则表达式条件的文本行 cut 截取文件中的特定字段 paste 附加字段 tr 字符转换或压缩 sort 调整文本行的顺序,使其符合特定准则 uniq 找出重 ...

  9. Linux文件操作常用命令整理

    收集.整理日常系统管理或维护当中的,常用到的一些关于文件操作的命令或需求,后续会慢慢补充.完善! 查看.生成指定目录的目录树结构?   [root@DB-Server ~]#tree   #当前目录 ...

随机推荐

  1. openstack network

  2. error日志

    2016/06/15 微信调核心时通用意外险 2016-06-15 11:44:23,771>>INFO >> com.isoftstone.core.service.comm ...

  3. area标签circle/rect/poligon坐标

    <img src="images/02.gif" title="flower" usemap="#mm" /> <map ...

  4. 技术文集:万能WINDOWS XP封装

    这里将系统封装分为3步:做系统.封装.部署 一.做系统 平台不限,但不建议在虚拟机上制作.CPU及主板芯片没有限制,关于intelide和intelppm的不兼容问题,深度白金3in1并没有删除这些注 ...

  5. Codeforces 627 A. XOR Equation (数学)

    题目链接:http://codeforces.com/problemset/problem/627/A 题意: 告诉你s 和 x,a + b = s    a xor b = x   a, b > ...

  6. HDU 3364 Lanterns (高斯消元)

    题意:有n个灯和m个开关,每个开关控制数个灯的状态改变,给出k条询问,问使灯的状态变为询问中的状态有多少种发法. 析:同余高斯消元法,模板题,将每个开关控制每个灯列成行列式,最终状态是结果列,同余高斯 ...

  7. 使用virtualenvwrapper隔离python环境

    今天使用的是virtualenvwrapper,相较于使用virtualenv, 好处就是把所有环境都放在同一目录下管理,以便更好的管理及切换. 首先需要确认有 pip 是否已经安装, 若没有需要先安 ...

  8. 32.怎样在Swift中实现TabBar和导航视图结合的项目?

    导航栏和TabBar结合的项目,在我们平常开发中会经常看到,下面我们通过自定义的TabBar来实现一个导航和TabBar结合的Demo. 1.自定义TabBar import UIKit class ...

  9. ref和out的使用与区别

    out的使用 ————————————————————————————————————————————————— class Program    {        static void Main( ...

  10. arm_cm4.c关于kinetis的修改

    /***********************************************************************/ /* * Initialize the NVIC t ...