本篇文章将会介绍who
命令的工作原理,介绍了utmp
文件和相应结构体,以及我们可以如何用简单代码将其实现,重点在如何查询和了解相关调用的过程。
当然最后实现的是不带任何参数的版本,否则过于复杂就偏离了学习系统编程的初心,而完全变成了几乎没有实用意义的造轮子。相较于上篇文章中ls -l
的实现,who
的实现更为简单。
1 who 的作用
who
可以知道正在使用系统的用户信息
1 | $ who |
每一行代表一个已经登录的用户,第一列是用用户名,第二列是终端名,第三列是登录时间,第四列是用户登录地址。
2 who 如何工作
可以使用man
来查看相关文档
1 | man who |
可以看到类似
1 | If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common. |
可知who
是从utmp
文件中读取用户登录的信息。
然后使用
1 | man -k utmp |
查看相关帮助
1 | $ man -k utmp |
找到utmp(5)
这一行,可知utmp
与之相关,然后输入
1 | man 5 utmp |
可以看到
1 | NAME |
故而可知可以使用struct utmp
来存放从utmp
文件中读取的数据,且其成员就是who
要展示的信息。
3 who 的代码实现
3.1 who的1.0版本
1 |
|
其中ERR_EXIT(m)
为一个宏,定义如下
1 |
|
编译执行后结果如下
1 | $ ./who1 |
系统的who
命令执行结果如下
1 | $ who |
可见其存在两个问题
- 时间显示不正确
- 显示了多余的非当前用户的信息
在2.0版的实现中可以解决该问题
3.2 who的2.0版本
主要做两点改进
- 使用
ctime()
来将time_t
格式的时间转换为正常时间 - 通过
utmp
结构中的ut_type
是否定义为USER_PROCESS
来判断是否是用户登录信息
代码如下
1 |
|
执行结果为
1 | ./who2 |
可以看见此时结果与系统命令who
输出的结果相当接近了。
3.3 who的实现3.0版本
上述版本实现简单明了,但每次只从utmp
文件中使用read()
读取一个用户的数据,如果该主机同时有多个用户使用,就需要调用许多次read()
,从而导致程序性能低下。
可以使用一个数组,然后用read()
一次性读取的n个用户的信息,然后每次返回一个用户供输出信息,当将数组中存放的数据都输出完以后,再重新读取n个用户信息到数组中,直到输出完所有信息。
各个功能的函数实现如下,文件utmplib.c
1 |
|
主函数实现如下
1 |
|
4 结束语
在本篇文章中,主要记录了who
命令的工作原理,了解了utmp
文件的作用,以及struct utmp
。在具体的实现过程中,循序渐进,从易到难实现了多个版本,其实这是因为who
的实现比上篇文章中ls -l
更简单,也在书中更前面的位置。因此这里重在学会如何查询和学习命令的方法。