一 总概
- 内存中的系统空间存放系统相关的程序与数据;用户空间存放用户相关的程序与数据。
- 内存中的用户空间里的应用程序通过内核与外界(用户)进行通信(而内核与外界通讯时,还要通过一层shell)。
- bin目录下一般存放命令的二进制文件
- utmp文件中保存的是当前正在本系统中的用户的信息。
wtmp文件中保存的是登录过本系统的用户的信息。
--------如何保存?如何读取?--------未完待续
参考博客:who命令编写的详细思路
二 查找who命令的来源
- 通过
man who
查找到who格式为:who [OPTION]... [ FILE | ARG1 ARG2 ]
FILE为–指定从某文件中读取用户信息
下面还有这样一行话:
If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is
common. If ARG1 ARG2 given, -m presumed: ‘am i’ or ‘mom likes’ are
usual.
意思是如果FILE没有指定,默认从 /var/run/utmp中读取用户数据(who一般情况下都是从这里读取用户数据)。
- 那么utmp文件在哪里呢,它的相关信息又该如何获取呢?
依旧是man命令的使用:man -k utmp
man的-k参数依据关键字模糊查找
得到如下:
可以发现与登录记录有关的只有utmp(5)这个东西。那么这是个什么东西呢?
其实就是linux的文件手册,utmp(5)代表utmp这本手册中的第5章节。 - 那么如何打开这一章节呢?
依旧是man(manual缩写,该命令就是查找手册具体内容的)命令 :man 5 utmp
可以发现,who命令就是从该utmp文件中读取用户信息的。
这里可以看出:这个utmp文件就是一个存储utmp结构体的数组(所以在读取该文件中用户信息时,自然想到要从该数组中读取,既然是从数组中读取,那思路就很清晰了,c语言读取结构体数组都理解原理吧,直接套用即可,每次读取定字节数(与utmp结构体大小一致)的数据长度赋给一个utmp结构体,就读取成功),而这个utmp结构体(存储用户的信息)又在utmp.h这个头文件中。 - 问题又来了,utmp.h文件在哪呢?
通过查找文件的命令find:find /usr/include -name "utmp.h"
(在unix系统中,很多头文件都放在usr/include/中)
得到:
真正的结构体具体内容是定义在第一个文件里面的,外面的是更外层的声明。 - 现在找到了utmp文件的手册,也知道了我们编写who命令是要从该文件中读取信息,那么该文件的地址在手册中有吗?当然!
第一个路径就是其绝对路径,在使用open()函数直接输入这个即可。 - 而在unix中这些很常用的路径一般都是已经封装好的宏定义,我们直接调用宏定义即可调用该地址,那么该地址又在哪里呢?
一般都放在相关的头文件中,那么回到第4部,进入/usr/include/utmp.h中,发现:
canonical:标准的典型的;compatibility:兼容性
在这中间有个UTMP_PATH宏定义很可能就是,那么接下来就是去查找_PATH_UTMP在哪个文件里面定义了。
利用在文件中查找具体文本的命令grep:grep "_PATH_UTMP" /usr/include/*.h
当不确定在哪个目录下时,也可使用:grep "_PATH_UTMP" /usr/*/*.h"
—正则表达式的巧妙使用
双引号带不带无所谓,默认都是查找字符串
可以发现在paths.h中有_PATH_UTNP宏定义以及其路径,查找成功!
三 who命令的编写
版本一:
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
void show_info(struct utmp* pUtmp);
int main()
{
int utmpfd;
struct utmp cur_log_rcd;
int rd_len = sizeof(struct utmp);
if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1)//这里就是对UTMP_FILE的使用,它在tump.h头文件中被宏定义过一次。打开了utmp文件,文件描述符赋给了utmpfd
{
perror("sadasd");
exit(1);
}
while (read(utmpfd, &cur_log_rcd, rd_len) == rd_len)
{
show_info(&cur_log_rcd);
}
close(utmpfd);
return 0;
}
void show_info(struct utmp* pUtmp)
{
printf("%-13.13s", pUtmp->ut_user);
printf("%-8.8s", pUtmp->ut_line);
printf("%-14.10ld", pUtmp->ut_tv.tv_sec);
printf("(%s)", pUtmp->ut_host);
printf("\n");
}
好,接下来问题来了。。。。。。。超难受的各种问题开始出现,花了七八小时午饭都没出就为了debug,详见博客:IT似海(和本文编写who命令没有大关系,不想看可直接跳过)。
版本二–
#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
void show_info(struct utmp* pUtmp);
int main()
{
int utmpfd;
struct utmp cur_log_rcd;
int rd_len = sizeof(struct utmp);
printf("sad!!!\n");
if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1)
{
perror("sadasd\n");
exit(1);
}
while (read(utmpfd, &cur_log_rcd, rd_len) == rd_len)
{
show_info(&cur_log_rcd);
}
close(utmpfd);
//getchar();
return 0;
}
void show_info(struct utmp* pUtmp)
{
time_t tm;
tm = time(NULL);//获取当前的总时间秒数
printf("%ld\n",tm);
printf("%ld\n",pUtmp->ut_time);//打印用户登录时的总时间秒数
printf("calendar times = %lu seconds\n", tm);
printf("localtime(&tm):\t%s", asctime(localtime(&tm)));
printf("gmtime(&tm):\t%s", asctime(gmtime(&tm)));
printf("ctime:\t\t%s\n", ctime(&tm));
//下面才为用户时间:
if (pUtmp->ut_type != USER_PROCESS)
return;
printf("%-13.13s", pUtmp->ut_user);
printf("%-8.8s", pUtmp->ut_line);
char * ps;
//下面这里,两种ps求得的时间秒数是不一样的,第二种是正确的,猜测可能是因为强制转换过程中出现了问题,需要深入探究一些
//ps=ctime((time_t*)&(pUtmp->ut_time));
tm=pUtmp->ut_time;
//ps=asctime(localtime(&tm));
printf("%14.19s",ps);
printf("(%s)", pUtmp->ut_host);
printf("\n");
}
四 总结回顾
- 对存储用户登录信息utmp结构体中各参数的认识
- 对一些头文件的位置以及内容有所了解—很多都在usr/include目录下
- 对查找某些文件以及某些文件内容的方法有了了解,利用find和grep命令
- 一些函数的认识与使用—open函数(注意与fopen的区别,见博客详细区别。open与read,write配合使用;fopen与fread,fwrite配合,open只能打开二进制文件,可见utmp文件就是二进制类型的),read函数,ctime,time
- 对who的工作原理有所认识,从utmp文件中读取用户信息,而utmp结构体是在utmp.h中定义的,注意区分
- 通过联机帮助,查询手册获得详细信息
- 强制类型转换对最后转换是否会有影响???
- vscode&远程链接&glibc库