who命令
列出已经登录的用户
[用户名][终端名][登陆时间][登陆地址]
实现思路
- 查看Linux系统who命令的信息
man who
Linux用户信息存储在/var/log/wtmp文件中 - 查看utmp头文件 man utmp
/var/log/wtmp中的数据使用utmp结构体的形式存储
3. who的工作流程
graph TD
A[打开文件] --> B[循环读取];
B -- Yes --> C[打印用户信息];
C --> B;
B -- No --> D[End];
代码
// who.h文件
#ifndef MYWHO_H__
#define MYWHO_H__
#define FILE_NAME "/var/log/wtmp"
void show_time(long);
void show_utmp_info(struct utmp *);
int who();
#endif
// who.c文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
void show_time(long timeval) {
char *cp;
cp = ctime(&timeval);
printf("%12.12s ", cp + 4);
}
void show_utmp_info(struct utmp *p_utmp_info) {
// #define USER_PROCESS 7
// ut_type = 7的用户为已登录的用户
if(p_utmp_info -> ut_type != USER_PROCESS)
return;
printf("%-8.8s ", p_utmp_info -> ut_name);
printf("%-8.8s ", p_utmp_info -> ut_line);
show_time(p_utmp_info -> ut_time);
printf("(%s) ", p_utmp_info -> ut_host);
printf("\n");
}
int who() {
int utmp_fd;
struct utmp utmp_info;
int utmp_len = sizeof(struct utmp);
if((utmp_fd = open(FILE_NAME, O_RDONLY)) == -1) {
perror(FILE_NAME);
return -1;
}
while(read(utmp_fd, &utmp_info, utmp_len) == utmp_len) {
show_utmp_info(&utmp_info);
}
close(utmp_fd);
return 0;
}
// main.c
#include <stdio.h>
#include <stdlib.h>
#include "who.h"
int main(void) {
if(who() == -1) {
exit(1);
}
exit(0);
}
makefile文件
OBJS=main.o who.o
CC=gcc
CFLAGS+=-c -Wall -g -o
who:$(OBJS)
$(CC) $(OBJS) -o who
%.o:%.c
$(CC) $^ $(CFLAGS) $@
clean:
rm who *.o -rf
使用localtime函数显示时间
void show_time(long timeval) {
struct tm *current_tm = localtime(&timeval);
printf("%d-%d-%d %d:%d:%d ", current_tm -> tm_year + 1900, current_tm -> tm_mon + 1, current_tm -> tm_mday, current_tm -> tm_hour, current_tm -> tm_min, current_tm -> tm_sec);
}
使用缓冲区优化代码
使用缓冲区的目的:减少系统调用次数
磁盘只能被操作系统操作,程序中的open、read、write都是告诉操作系统要发起一个系统调用,而不是直接在程序中操作磁盘,这个过程中就需要将控制权从用户程序切换到操作系统,这个切换过程涉及到CPU切到用户模式、堆栈、内存环境切换
所以系统调用次数越多,切换越频繁,那么消耗在切换过程中的时间越多
// who.h
#ifndef MYWHO_H__
#define MYWHO_H__
#include <stdio.h>
#include <stdlib.h>
#include <utmp.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_NAME "/var/run/utmp"
#define LEN sizeof(struct utmp)
#define SIZE 16
int utmp_open(); // 打开/var/run/utmp文件
int utmp_load(); // 将数据加载到缓冲区
struct utmp *utmp_next(); // 获取缓冲区下一条数据
void utmp_close(); // 关闭/var/run/utmp文件
void show_time(long timeval); // 时间打印
void show_utmp_info(struct utmp *p_utmp); // 打印utmp结构体
int who();
#endif
// who.c
#include "who.h"
static int utmp_fd = -1;
static struct utmp buffer[SIZE];
static int total_num = 0;
static int cur_num = 0;
int utmp_open() {
utmp_fd = open(FILE_NAME, O_RDONLY);
total_num = cur_num = 0;
return utmp_fd;
}
int utmp_load() {
int read_chars = read(utmp_fd, buffer, SIZE * LEN);
if(read_chars < 0)
return -1;
total_num = read_chars / LEN;
cur_num = 0;
return total_num;
}
struct utmp *utmp_next() {
struct utmp *res = NULL;
if(utmp_fd == -1)
return NULL;
if(total_num == cur_num && utmp_load() == 0)
return NULL;
res = &buffer[cur_num];
cur_num ++;
return res;
}
void utmp_close() {
if(utmp_fd != -1)
close(utmp_fd);
}
void show_time(long timeval) {
struct tm *current_tm = localtime(&timeval);
printf("%d-%d-%d %d:%d:%d ", current_tm -> tm_year + 1900, current_tm -> tm_mon + 1, current_tm -> tm_mday, current_tm -> tm_hour, current_tm -> tm_min, current_tm -> tm_sec);
}
void show_utmp_info(struct utmp *p_utmp) {
if(p_utmp -> ut_type != USER_PROCESS)
return;
printf("%-8.8s ", p_utmp -> ut_name);
printf("%-8.8s ", p_utmp -> ut_line);
show_time(p_utmp -> ut_time);
printf("(%s)", p_utmp -> ut_host);
printf("\n");
}
int who() {
struct utmp *p_utmp = NULL;
if(utmp_open() == -1) {
perror(FILE_NAME);
return -1;
}
while((p_utmp = utmp_next()) != NULL)
show_info(p_utmp);
utmp_close();
return 0;
}