当前位置: 首页 > news >正文

Linux who命令实现:文件读写与系统编程实践

1. 从零实现Linux who命令:深入理解文件读写与系统编程

作为一个常年与Linux打交道的开发者,我始终认为理解系统命令的实现原理是提升编程能力的最佳途径。今天我们就来解剖who这个看似简单却内涵丰富的命令,通过亲手实现它来掌握Linux文件操作的核心机制。

在终端输入who命令时,系统会列出当前所有登录用户的信息。这背后其实是一个典型的文件读取过程 - who程序通过解析/var/run/utmp这个特殊文件来获取登录信息。下面我将带您从内核数据结构到实际代码,一步步构建我们自己的who实现。

2. 理解utmp文件结构

2.1 utmp文件的作用与位置

Linux系统通过两个关键文件记录用户登录信息:

  • /var/run/utmp:记录当前登录用户会话
  • /var/log/wtmp:记录历史登录信息(需要root权限访问)

这些文件不是普通文本文件,而是由固定大小的二进制记录组成。每个登录会话对应一条记录,存储在utmp结构体中。我们可以通过查看/usr/include/utmp.h头文件来了解其定义:

#define UT_NAMESIZE 32 #define UT_LINESIZE 32 struct utmp { short ut_type; // 记录类型 pid_t ut_pid; // 进程ID char ut_line[UT_LINESIZE]; // 终端设备名 char ut_user[UT_NAMESIZE]; // 用户名 struct timeval ut_tv; // 时间戳 // ...其他字段省略 };

2.2 关键字段解析

特别需要注意ut_type字段,它决定了记录的类型:

  • USER_PROCESS(7):普通用户登录会话
  • BOOT_TIME(2):系统启动记录
  • DEAD_PROCESS(8):已终止的会话

实际编程时我们发现,utmp文件中包含各种类型的记录,但who命令只显示USER_PROCESS类型的记录。这是第一个需要处理的细节。

3. 实现文件读取逻辑

3.1 文件操作三部曲

Linux文件操作遵循经典的三步模式:

  1. open() - 打开文件获取文件描述符
  2. read() - 循环读取数据
  3. close() - 释放资源
#include <fcntl.h> #include <unistd.h> int main() { int fd = open("/var/run/utmp", O_RDONLY); if(fd == -1) { perror("open utmp failed"); return 1; } struct utmp record; while(read(fd, &record, sizeof(record)) == sizeof(record)) { // 处理每条记录 } close(fd); return 0; }

3.2 读取过程中的关键细节

  • 文件打开模式必须使用O_RDONLY,因为utmp文件通常不允许写入
  • read()的返回值需要严格检查,它可能小于请求的字节数
  • 每次读取的字节数必须精确等于sizeof(struct utmp),否则记录会错位

我在实际测试中发现,如果read长度参数不正确,会导致后续所有记录解析错误。这是一个典型的二进制文件处理陷阱。

4. 完善信息显示功能

4.1 过滤无效记录

根据ut_type字段过滤掉非用户会话记录:

void show_info(struct utmp *ut) { if(ut->ut_type != USER_PROCESS) return; printf("%-8s %-8s", ut->ut_user, ut->ut_line); // 显示其他信息... }

4.2 时间格式转换

utmp中存储的时间是从1970年1月1日开始的秒数(time_t),需要转换为可读格式:

#include <time.h> void show_time(time_t tv) { struct tm *tm = localtime(&tv); printf("%04d-%02d-%02d %02d:%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); }

注意:tm_year是从1900年开始的年数,tm_mon从0开始计数,这些细节容易出错。

5. 完整实现代码

以下是经过优化的完整实现:

#include <stdio.h> #include <utmp.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <stdlib.h> #define SHOW_HOSTNAME 1 void show_time(time_t tv) { struct tm *tm = localtime(&tv); printf("%04d-%02d-%02d %02d:%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); } void show_info(struct utmp *ut) { if(ut->ut_type != USER_PROCESS) return; printf("%-8s %-8s ", ut->ut_user, ut->ut_line); show_time(ut->ut_tv.tv_sec); #if SHOW_HOSTNAME if(ut->ut_host[0] != '\0') printf(" (%s)", ut->ut_host); #endif putchar('\n'); } int main() { int fd = open(UTMP_FILE, O_RDONLY); if(fd == -1) { perror(UTMP_FILE); exit(1); } struct utmp rec; while(read(fd, &rec, sizeof(rec)) == sizeof(rec)) { show_info(&rec); } close(fd); return 0; }

编译与测试:

gcc -o mywho mywho.c ./mywho

6. 常见问题与调试技巧

6.1 权限问题处理

当程序无法打开utmp文件时:

  1. 检查文件路径是否正确(不同Linux发行版可能不同)
  2. 确保程序有读取权限(普通用户通常可以读取utmp)

6.2 结构体对齐问题

如果遇到字段显示异常,可能是结构体对齐问题:

  • 使用#pragma pack(1)强制1字节对齐
  • 或者检查头文件版本是否匹配

6.3 时间显示异常

如果时间显示不正确:

  1. 检查时区设置(/etc/localtime
  2. 确保系统时间同步(ntpdate命令)

7. 扩展思考与优化方向

7.1 性能优化

对于大型系统,utmp文件可能包含数千条记录:

  • 使用缓冲读取(readv)提高效率
  • 考虑只读取最新记录(从文件尾部反向读取)

7.2 功能扩展

可以增强原始who命令的功能:

  • 添加登录时长统计
  • 支持按用户筛选
  • 输出彩色化显示

7.3 跨平台兼容

不同Unix-like系统的utmp实现略有差异:

  • 使用autoconf检测系统特性
  • 为不同平台提供条件编译

通过这个项目,我们不仅实现了who命令的核心功能,更重要的是掌握了Linux系统编程中文件操作的精髓。这种通过逆向分析系统命令来学习编程的方法,可以应用到许多其他Linux命令的研究中。

http://www.jsqmd.com/news/578740/

相关文章:

  • TGP Ecran:Arduino OLED显示库的轻量封装与非阻塞刷新设计
  • ESP32-Arduino IDE的开发学习记录(二)显示屏
  • 为什么你的C++量子模拟器总在2^10后崩溃?内存优化、张量压缩与SIMD加速三重方案揭秘
  • RPlatform教育机器人运动控制库详解
  • 群晖 /dev/md0 根分区爆满 100% 排查清理全流程
  • 拯救996:OpenClaw+百川2-13B量化模型自动生成周报
  • OpenClaw社交媒体管理:Gemma-3-12b-it自动回复评论与生成周报
  • 从一次RDP爆破到全网挖矿:复盘Windows Server 3389端口的安全加固与监控策略
  • DOCX转LaTeX:从繁琐排版到学术自动化的无缝过渡
  • OpenClaw+Qwen3-14b_int4_awq:跨平台文件同步助手
  • 端边云协同,全域智治——奥尔特云智慧安保解决方案
  • 短视频 SEO 优化能给企业带来什么好处_短视频 SEO 如何优化视频标题和描述
  • 6月PMP考试紧急冲刺指南:没好好备考?这样做还能逆风翻盘
  • 头歌实践教学-NLP:基于决策树与正则表达式的词法分析实战
  • OpenClaw跨平台对比:Windows/macOS对接Qwen3-14B差异详解
  • 依赖p4est库的程序windows运行方法----支持vs2022调试
  • 独立站建站成本全解析
  • TC630 3BSE002253R1 控制器模块
  • Cuvil + HuggingFace Pipeline端到端加速实录:BERT-base推理延迟从142ms降至31ms的6个关键编译开关
  • Transformer架构详细解读(教程向)
  • Go的select语句:多路复用的channel操作
  • SEO优化和网络推广的关系是什么_如何利用知识付费平台进行网络推广
  • 2026年山东省首版次高端软件申报已经开始,中承信安助力企业快速申报
  • 音频转换与格式解密工具全解析:破解QQ音乐限制的批量处理方案
  • 从零开始学Flink:TopN 榜单
  • 从LVGL菜单组件反推:手搓一个轻量级C语言菜单框架(适合RTOS/单片机)
  • 本科毕业论文“通关秘籍”:好写作AI的神奇助力
  • 主流CRM系统盘点与选型:电商零售企业2026实战指南
  • STM32开发中的可执行文件格式解析:AXF、HEX与BIN
  • 8位单片机高效处理16位整数的4种方法