跨平台系统信息获取库libsigar源码分析系列(一)

admin 2026-03-17 07:00:38 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档详细分析了跨平台库libsigar在Linux平台的源码实现,核心在于利用/proc和/sys文件系统获取系统信息。文章解析了关键数据结构sigar_t与linux_proc_stat_t,阐述了初始化流程及磁盘I/O统计方法检测机制。重点探讨了内存、CPU及进程监控模块的实现细节,如通过/proc/meminfo获取内存状态、解析/proc/stat计算CPU使用率以及读取/proc下的进程状态文件。内容为理解系统底层监控技术与安全工具依赖库原理提供了详尽参考。 综合评分: 88 文章分类: 代码审计,安全开发,安全工具


cover_image

跨平台系统信息获取库libsigar源码分析系列(一)

原创

haidragon haidragon

安全狗的自我修养

2026年3月16日 12:14 湖南

源码分析mettle后门工具学习 所使用的依赖库

官网:http://securitytech.cc

#

libsigar Linux 平台源码分析

概述

libsigar 在 Linux 平台的实现主要通过 /proc 文件系统和 /sys 文件系统获取系统信息。与 Windows 和 macOS 不同,Linux 提供了统一的 procfs 接口,使得系统信息的获取相对简单和统一。

核心架构

sigart 结构体 (sigaros.h:59-70)

1. structsigar_t{
2. SIGAR_T_BASE;
3. int pagesize;// 页面大小的位移值
4. int ram;// 物理内存大小 (缓存)
5. int proc_signal_offset;// 进程信号字段偏移量
6. linux_proc_stat_t last_proc_stat;// 进程统计信息缓存
7. int lcpu;// 每个物理 CPU 的逻辑 CPU 数
8. linux_iostat_e iostat;// 磁盘 I/O 统计类型
9. char*proc_net;// 自定义 /proc/net 路径
10. int has_nptl;// 是否支持 NPTL (Native POSIX Thread Library)
11. };

linuxprocstatt 结构体 (sigaros.h:33-50)

1. typedefstruct{
2. sigar_pid_t pid;// 进程 ID
3. time_t mtime;// 最后修改时间 (缓存过期检查)
4. sigar_uint64_t vsize;// 虚拟内存大小
5. sigar_uint64_t rss;// 常驻集大小
6. sigar_uint64_t minor_faults;// 次要缺页错误
7. sigar_uint64_t major_faults;// 主要缺页错误
8. sigar_uint64_t ppid;// 父进程 ID
9. int tty;// 终端编号
10. int priority;// 优先级
11. int nice;// nice 值
12. sigar_uint64_t start_time;// 启动时间
13. sigar_uint64_t utime;// 用户 CPU 时间
14. sigar_uint64_t stime;// 系统 CPU 时间
15. char name[SIGAR_PROC_NAME_LEN];// 进程名称
16. char state;// 进程状态
17. int processor;// 最后运行的处理器
18. }linux_proc_stat_t;

核心技术

1. /proc 文件系统

libsigar Linux 实现的核心是 /proc 文件系统,这是一个虚拟文件系统,提供内核和进程信息。

关键路径 (linux_sigar.c:54-68)

1. staticchar*gPROC_FS_ROOT;// "/proc"
2. staticchar*gPROCP_FS_ROOT;// "/proc/"
3. staticchar*gPROC_MEMINFO;// "/proc/meminfo"
4. staticchar*gPROC_VMSTAT;// "/proc/vmstat"
5. staticchar*gPROC_MTRR;// "/proc/mtrr"
6. staticchar*gPROC_STAT;// "/proc/stat"
7. staticchar*gPROC_UPTIME;// "/proc/uptime"
8. staticchar*gPROC_LOADAVG;// "/proc/loadavg"
9. staticchar*gPROC_DISKSTATS;// "/proc/diskstats"
10. staticchar*gPROC_PARTITIONS;// "/proc/partitions"
11. staticchar*gSYS_BLOCK;// "/sys/block"
12. staticchar*gMOUNTED;// "/etc/mtab"

2. 初始化流程 (linux_sigar.c:176-236)

1. int sigar_os_open(sigar_t**sigar)
2. {
3. // 1. 设置 proc 文件路径
4. set_proc_locations();

6. // 2. 分配 sigar 结构
7. *sigar = malloc(sizeof(**sigar));

9. // 3. 计算页大小位移值
10. i = getpagesize();
11. while((i >>=1)>0){
12. (*sigar)->pagesize++;
13. }

15. // 4. 获取系统启动时间
16. status = sigar_boot_time_get(*sigar);

18. // 5. 获取系统时钟频率
19. (*sigar)->ticks = sysconf(_SC_CLK_TCK);

21. // 6. 检测可用的磁盘 I/O 统计方法
22. if(stat(gPROC_DISKSTATS,&sb)==0){
23. (*sigar)->iostat = IOSTAT_DISKSTATS;// 2.6 内核
24. }
25. elseif(stat(gSYS_BLOCK,&sb)==0){
26. (*sigar)->iostat = IOSTAT_SYS;// 2.6 内核 /sys
27. }
28. elseif(stat(gPROC_PARTITIONS,&sb)==0){
29. (*sigar)->iostat = IOSTAT_PARTITIONS;// 2.4 内核
30. }
31. else{
32. (*sigar)->iostat = IOSTAT_NONE;
33. }

35. // 7. 检测 NPTL 支持 (2.6+ 内核)
36. uname(&name);
37. kernel_rev = atoi(&name.release[2]);
38. if(kernel_rev >=6){
39. has_nptl =1;
40. }
41. else{
42. has_nptl = getenv("SIGAR_HAS_NPTL")?1:0;
43. }

45. return SIGAR_OK;
46. }

核心功能模块

1. 内存监控

内存信息获取 (linux_sigar.c:346-376)

1. int sigar_mem_get(sigar_t*sigar,sigar_mem_t*mem)
2. {
3. // 读取 /proc/meminfo
4. // MemTotal:   总物理内存
5. // MemFree:    空闲内存
6. // Buffers:    缓冲区
7. // Cached:     页面缓存

9. mem->total  = sigar_meminfo(buffer, MEMINFO_PARAM("MemTotal"));
10. mem->free   = sigar_meminfo(buffer, MEMINFO_PARAM("MemFree"));
11. mem->used   = mem->total - mem->free;

13. buffers = sigar_meminfo(buffer, MEMINFO_PARAM("Buffers"));
14. cached  = sigar_meminfo(buffer, MEMINFO_PARAM("Cached"));

16. kern = buffers + cached;
17. mem->actual_free = mem->free + kern;// 实际可用内存
18. mem->actual_used = mem->used - kern;// 实际已用内存

20. // 尝试从 MTRR 获取物理内存大小
21. get_ram(sigar, mem);

23. return SIGAR_OK;
24. }

MTRR 内存检测 (linux_sigar.c:258-318)

1. staticint get_ram(sigar_t*sigar,sigar_mem_t*mem)
2. {
3. // 读取 /proc/mtrr (Memory Type Range Registers)
4. // write-back 寄存器总和等于总物理内存

6. if(!(fp = fopen(gPROC_MTRR,"r"))){
7. return errno;
8. }

10. while((ptr = fgets(buffer,sizeof(buffer), fp))){
11. if(strstr(ptr,"size=")&& strstr(ptr,"write-back")){
12. ptr +=5;// 跳过 "size="
13. total += atoi(ptr);
14. }
15. }

17. if((total - sys_total)>256){
18. // MTRR 寄存器总和偏差太大,忽略
19. total =0;
20. }

22. mem->ram = sigar->ram = total;
23. return SIGAR_OK;
24. }

交换空间信息 (linux_sigar.c:378-424)

1. int sigar_swap_get(sigar_t*sigar,sigar_swap_t*swap)
2. {
3. // 从 /proc/meminfo 读取交换空间
4. swap->total  = sigar_meminfo(buffer, MEMINFO_PARAM("SwapTotal"));
5. swap->free   = sigar_meminfo(buffer, MEMINFO_PARAM("SwapFree"));
6. swap->used   = swap->total - swap->free;

8. // 从 /proc/vmstat (2.6+) 读取分页统计
9. // pswpin:  换入页数
10. // pswpout: 换出页数

12. // 或从 /proc/stat (2.2, 2.4) 读取
13. // swap: 换入换出统计

15. return SIGAR_OK;
16. }

2. CPU 监控

CPU 统计 (linux_sigar.c:426-462)

1. staticvoid get_cpu_metrics(sigar_t*sigar,sigar_cpu_t*cpu,char*line)
2. {
3. // /proc/stat 格式:
4. // cpu  user nice system idle iowait irq softirq steal

6. cpu->user += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
7. cpu->nice += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
8. cpu->sys  += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
9. cpu->idle += SIGAR_TICK2MSEC(sigar_strtoull(ptr));

11. // 2.6+ 内核
12. if(*ptr ==' '){
13. cpu->wait += SIGAR_TICK2MSEC(sigar_strtoull(ptr));// iowait
14. cpu->irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr));// irq
15. cpu->soft_irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr));// softirq
16. }

18. // 2.6.11+ 内核
19. if(*ptr ==' '){
20. cpu->stolen += SIGAR_TICK2MSEC(sigar_strtoull(ptr));// steal (虚拟化)
21. }

23. cpu->total = user + nice + sys + idle + wait + irq + soft_irq + stolen;
24. }

CPU 列表 (linux_sigar.c:464-517)

1. int sigar_cpu_list_get(sigar_t*sigar,sigar_cpu_list_t*cpulist)
2. {
3. // 读取 /proc/stat 中的 cpu0, cpu1, ...
4. // 支持超线程核心合并

6. if(core_rollup &&(i % sigar->lcpu)){
7. // 合并逻辑处理器的时间
8. cpu =&cpulist->data[cpulist->number-1];
9. }
10. else{
11. // 创建新的 CPU 条目
12. SIGAR_CPU_LIST_GROW(cpulist);
13. cpu =&cpulist->data[cpulist->number++];
14. }

16. get_cpu_metrics(sigar, cpu, ptr);
17. }

CPU 信息 (linux_sigar.c:1784-1822)

1. int sigar_cpu_info_list_get(sigar_t*sigar,sigar_cpu_info_list_t*cpu_infos)
2. {
3. // 读取 /proc/cpuinfo
4. // processor : 0
5. // vendor_id : GenuineIntel
6. // model name : Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
7. // cpu MHz : 2400.000
8. // cache size : 4096 KB

10. // 从 /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq 获取最大频率
11. // 从 /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq 获取最小频率

13. return SIGAR_OK;
14. }

3. 进程监控

进程列表 (linux_sigar.c:666-707)

1. int sigar_os_proc_list_get(sigar_t*sigar,sigar_proc_list_t*proclist)
2. {
3. DIR*dirp = opendir(gPROCP_FS_ROOT);// 打开 /proc

5. while((ent = readdir(dirp))!= NULL){
6. if(!sigar_isdigit(*ent->d_name)){
7. continue;// 跳过非数字目录
8. }

10. // 检查是否为线程 (非 NPTL 模式)
11. if(threadbadhack &&
12. proc_isthread(sigar, ent->d_name, strlen(ent->d_name)))
13. {
14. continue;// 跳过线程
15. }

17. proclist->data[proclist->number++]=
18. strtoul(ent->d_name, NULL,10);
19. }

21. return SIGAR_OK;
22. }

线程检测 (linux_sigar.c:600-664)

1. static SIGAR_INLINE int proc_isthread(sigar_t*sigar,char*pidstr,int len)
2. {
3. // 检查 /proc/<pid>/stat 的 exit_signal 字段
4. // '17' == SIGCHLD == 真正的进程
5. // '33' 或 '0' == 线程

7. // /proc/self/stat 字段:
8. // 1-37: 标准字段
9. // 38: exit_signal

11. // 从后向前解析 exit_signal
12. while(offset-->0){
13. while((n&nbsp;>0)&&&nbsp;isdigit(buffer[n--]));// 跳过字段
14. while((n&nbsp;>0)&&!isdigit(buffer[n--]));// 跳过空格
15. }

17. ptr&nbsp;=&buffer[n];
18. if((*ptr++=='1')&&(*ptr++=='7')&&(*ptr++==' ')){
19. return0;// 真正的进程
20. }

22. return1;// 线程
23. }

进程状态读取 (linux_sigar.c:709-808)

1. staticint&nbsp;proc_stat_read(sigar_t*sigar,sigar_pid_t&nbsp;pid)
2. {
3. // 短期缓存: 60 秒内重复读取同一进程使用缓存
4. if(pstat->pid&nbsp;==&nbsp;pid){
5. if((timenow&nbsp;-&nbsp;pstat->mtime)<&nbsp;SIGAR_LAST_PROC_EXPIRE){
6. return&nbsp;SIGAR_OK;
7. }
8. }

10. // 读取 /proc/<pid>/stat
11. // 格式 (部分字段):
12. // 1 &nbsp;pid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; %d
13. // 2 &nbsp;comm &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;%s (进程名,可能被截断)
14. // 3 &nbsp;state &nbsp; &nbsp; &nbsp; &nbsp; %c
15. // 4 &nbsp;ppid &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;%d
16. // 14 utime &nbsp; &nbsp; &nbsp; &nbsp; %lu (用户时间,时钟周期)
17. // 15 stime &nbsp; &nbsp; &nbsp; &nbsp; %lu (系统时间,时钟周期)
18. // 22 starttime &nbsp; &nbsp; %lu (启动后的时钟周期数)
19. // 23 vsize &nbsp; &nbsp; &nbsp; &nbsp; %lu (虚拟内存大小)
20. // 24 rss &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; %lu (常驻集大小,页数)

22. // 解析进程名 (处理括号内的可能包含空格的进程名)
23. if(!(ptr&nbsp;=&nbsp;strchr(ptr,'('))){
24. return&nbsp;EINVAL;
25. }
26. if(!(tmp&nbsp;=&nbsp;strrchr(++ptr,')'))){
27. return&nbsp;EINVAL;
28. }
29. len&nbsp;=&nbsp;tmp-ptr;
30. memcpy(pstat->name,&nbsp;ptr,&nbsp;len);
31. pstat->name[len]='\0';

33. // 跳过其他字段...
34. // 解析关键信息

36. pstat->utime&nbsp;=&nbsp;SIGAR_TICK2MSEC(sigar_strtoull(ptr));// 用户时间 (毫秒)
37. pstat->stime&nbsp;=&nbsp;SIGAR_TICK2MSEC(sigar_strtoull(ptr));// 系统时间 (毫秒)

39. pstat->start_time &nbsp;=&nbsp;sigar_strtoul(ptr);// 时钟周期数
40. pstat->start_time&nbsp;/=&nbsp;sigar->ticks;// 转换为秒
41. pstat->start_time&nbsp;+=&nbsp;sigar->boot_time;// 加上启动时间
42. pstat->start_time&nbsp;*=1000;// 转换为毫秒

44. pstat->vsize&nbsp;=&nbsp;sigar_strtoull(ptr);// 虚拟内存 (字节)
45. pstat->rss &nbsp;&nbsp;=&nbsp;pageshift(sigar_strtoull(ptr));// 常驻集 (字节)

47. return&nbsp;SIGAR_OK;
48. }

进程内存 (linux_sigar.c:810-833)

1. int&nbsp;sigar_proc_mem_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_mem_t*procmem)
2. {
3. // 从缓存的 stat 读取缺页错误
4. procmem->minor_faults&nbsp;=&nbsp;pstat->minor_faults;
5. procmem->major_faults&nbsp;=&nbsp;pstat->major_faults;
6. procmem->page_faults&nbsp;=&nbsp;procmem->minor_faults&nbsp;+&nbsp;procmem->major_faults;

8. // 读取 /proc/<pid>/statm
9. // 格式: size resident shared text lib data dt
10. // size: &nbsp; &nbsp; 总虚拟内存大小 (页)
11. // resident: 常驻集大小 (页)
12. // shared: &nbsp; 共享内存页数

14. status&nbsp;=&nbsp;SIGAR_PROC_FILE2STR(buffer,&nbsp;pid,"/statm");

16. procmem->size &nbsp; &nbsp;&nbsp;=&nbsp;pageshift(sigar_strtoull(ptr));
17. procmem->resident&nbsp;=&nbsp;pageshift(sigar_strtoull(ptr));
18. procmem->share &nbsp; &nbsp;=&nbsp;pageshift(sigar_strtoull(ptr));

20. return&nbsp;SIGAR_OK;
21. }

进程磁盘 I/O (linux_sigar.c:845-861)

1. int&nbsp;sigar_proc_cumulative_disk_io_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,
2. sigar_proc_cumulative_disk_io_t*proc_cumulative_disk_io)
3. {
4. // 读取 /proc/<pid>/io (2.6.20+)
5. // read_bytes: &nbsp;读字节数
6. // write_bytes: 写字节数
7. // cancel_write_bytes: 取消写回的字节数

9. status&nbsp;=&nbsp;SIGAR_PROC_FILE2STR(buffer,&nbsp;pid,"/io");

11. proc_cumulative_disk_io->bytes_read&nbsp;=&nbsp;get_named_proc_token(buffer,"\nread_bytes");
12. proc_cumulative_disk_io->bytes_written&nbsp;=&nbsp;get_named_proc_token(buffer,"\nwrite_bytes");
13. proc_cumulative_disk_io->bytes_total&nbsp;=
14. proc_cumulative_disk_io->bytes_read&nbsp;+&nbsp;proc_cumulative_disk_io->bytes_written;

16. return&nbsp;SIGAR_OK;
17. }

进程凭据 (linux_sigar.c:865-900)

1. int&nbsp;sigar_proc_cred_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_cred_t*proccred)
2. {
3. // 读取 /proc/<pid>/status
4. // Uid: real euid saved setuid fsuid
5. // Gid: real egid saved setgid fsgid

7. status&nbsp;=&nbsp;SIGAR_PROC_FILE2STR(buffer,&nbsp;pid,&nbsp;PROC_PSTATUS);

9. if((ptr&nbsp;=&nbsp;strstr(buffer,"\nUid:"))){
10. ptr&nbsp;=&nbsp;sigar_skip_token(ptr);
11. proccred->uid &nbsp;=&nbsp;sigar_strtoul(ptr);// 真实 UID
12. proccred->euid&nbsp;=&nbsp;sigar_strtoull(ptr);// 有效 UID
13. }

15. if((ptr&nbsp;=&nbsp;strstr(ptr,"\nGid:"))){
16. ptr&nbsp;=&nbsp;sigar_skip_token(ptr);
17. proccred->gid &nbsp;=&nbsp;sigar_strtoul(ptr);// 真实 GID
18. proccred->egid&nbsp;=&nbsp;sigar_strtoull(ptr);// 有效 GID
19. }

21. return&nbsp;SIGAR_OK;
22. }

进程状态 (linux_sigar.c:943-997)

1. int&nbsp;sigar_proc_state_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_state_t*procstate)
2. {
3. // 从 stat 读取基本状态
4. procstate->state&nbsp;=&nbsp;pstat->state;// R|S|D|Z|T
5. procstate->ppid&nbsp;=&nbsp;pstat->ppid;
6. procstate->tty&nbsp;=&nbsp;pstat->tty;
7. procstate->priority&nbsp;=&nbsp;pstat->priority;
8. procstate->nice&nbsp;=&nbsp;pstat->nice;
9. procstate->processor&nbsp;=&nbsp;pstat->processor;

11. // 进程名处理: 如果被截断为 15 字符,尝试从命令行获取完整名称
12. if(strlen(pstat->name)==15){
13. if(sigar_procfs_args_get(sigar,&nbsp;pid,&procargs)==&nbsp;SIGAR_OK&nbsp;&&
14. procargs.number&nbsp;>=1){
15. procname&nbsp;=&nbsp;procargs.data[0];
16. }
17. }

19. // 从 status 读取线程数
20. ptr&nbsp;=&nbsp;strstr(buffer,"\nThreads:");
21. if(ptr){
22. ptr&nbsp;=&nbsp;sigar_skip_token(ptr);
23. procstate->threads&nbsp;=&nbsp;sigar_strtoul(ptr);
24. }

26. // 获取打开的文件描述符数量
27. sigar_proc_fd_t&nbsp;proc_fd_count;
28. if(sigar_proc_fd_get(sigar,&nbsp;pid,&proc_fd_count)==&nbsp;SIGAR_OK){
29. procstate->open_files&nbsp;=&nbsp;proc_fd_count.total;
30. }

32. return&nbsp;SIGAR_OK;
33. }

进程环境变量 (linux_sigar.c:1010-1067)

1. int&nbsp;sigar_proc_env_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_env_t*procenv)
2. {
3. // 读取 /proc/<pid>/environ
4. // 格式: key1=value1\0key2=value2\0...\0

6. if((fd&nbsp;=&nbsp;open(name,&nbsp;O_RDONLY))<0){
7. if(errno&nbsp;==&nbsp;ENOENT){
8. return&nbsp;ESRCH;// 进程不存在
9. }
10. return&nbsp;errno;
11. }

13. len&nbsp;=&nbsp;read(fd,&nbsp;buffer,sizeof(buffer));
14. close(fd);

16. buffer[len]='\0';
17. ptr&nbsp;=&nbsp;buffer;

19. end&nbsp;=&nbsp;buffer&nbsp;+&nbsp;len;
20. while(ptr&nbsp;<&nbsp;end){
21. char*val&nbsp;=&nbsp;strchr(ptr,'=');

23. if(val&nbsp;==&nbsp;NULL){
24. break;
25. }

27. klen&nbsp;=&nbsp;val&nbsp;-&nbsp;ptr;
28. SIGAR_SSTRCPY(key,&nbsp;ptr);
29. key[klen]='\0';
30. ++val;

32. vlen&nbsp;=&nbsp;strlen(val);
33. status&nbsp;=&nbsp;procenv->env_getter(procenv->data,&nbsp;key,&nbsp;klen,&nbsp;val,&nbsp;vlen);

35. if(status&nbsp;!=&nbsp;SIGAR_OK){
36. break;// 回调要求停止
37. }

39. ptr&nbsp;+=(klen&nbsp;+1+&nbsp;vlen&nbsp;+1);
40. }

42. return&nbsp;SIGAR_OK;
43. }

进程可执行文件信息 (linux_sigar.c:1078-1107)

1. int&nbsp;sigar_proc_exe_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_exe_t*procexe)
2. {
3. // /proc/<pid>/cwd -> 工作目录 (符号链接)
4. // /proc/<pid>/exe -> 可执行文件 (符号链接)
5. // /proc/<pid>/root -> 根目录 (符号链接)

7. (void)SIGAR_PROC_FILENAME(name,&nbsp;pid,"/cwd");
8. if((len&nbsp;=&nbsp;readlink(name,&nbsp;procexe->cwd,sizeof(procexe->cwd)-1))>=0){
9. procexe->cwd[len]='\0';
10. }

12. (void)SIGAR_PROC_FILENAME(name,&nbsp;pid,"/exe");
13. if((len&nbsp;=&nbsp;readlink(name,&nbsp;procexe->name,sizeof(procexe->name)-1))>=0){
14. procexe->name[len]='\0';
15. }

17. (void)SIGAR_PROC_FILENAME(name,&nbsp;pid,"/root");
18. if((len&nbsp;=&nbsp;readlink(name,&nbsp;procexe->root,sizeof(procexe->root)-1))>=0){
19. procexe->root[len]='\0';
20. }

22. // 通过 ELF 文件猜测架构
23. procexe->arch&nbsp;=&nbsp;sigar_elf_file_guess_arch(sigar,&nbsp;procexe->name);

25. return&nbsp;SIGAR_OK;
26. }

进程模块 (linux_sigar.c:1109-1151)

1. int&nbsp;sigar_proc_modules_get(sigar_t*sigar,sigar_pid_t&nbsp;pid,sigar_proc_modules_t*procmods)
2. {
3. // 读取 /proc/<pid>/maps
4. // 格式:
5. // address &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perms offset &nbsp;dev &nbsp; inode &nbsp; pathname
6. // 7f8c5c000000-7f8c5c010000 rw-p 00000000 00:00 0
7. // 7f8c5c010000-7f8c5c020000 r-xp 00000000 08:01 123456 /lib/libc.so.6

9. while((ptr&nbsp;=&nbsp;fgets(buffer,sizeof(buffer),&nbsp;fp))){
10. // 跳过前 4 个字段 (address, perms, offset, dev)
11. ptr&nbsp;=&nbsp;sigar_skip_multiple_token(ptr,4);
12. inode&nbsp;=&nbsp;sigar_strtoul(ptr);

14. // inode 为 0 表示匿名映射,跳过
15. if((inode&nbsp;==0)||(inode&nbsp;==&nbsp;last_inode)){
16. last_inode&nbsp;=0;
17. continue;
18. }

20. last_inode&nbsp;=&nbsp;inode;
21. ptr&nbsp;=&nbsp;strrchr(ptr,' ')+1;// 路径名在最后
22. len&nbsp;=&nbsp;strlen(ptr);
23. ptr[len-1]='\0';// 去掉换行符

25. status&nbsp;=&nbsp;procmods->module_getter(procmods->data,&nbsp;ptr,&nbsp;len-1);

27. if(status&nbsp;!=&nbsp;SIGAR_OK){
28. break;
29. }
30. }

32. return&nbsp;SIGAR_OK;
33. }

4. 网络监控

网络接口统计 (linux_sigar.c:1946-2016)

1. int&nbsp;sigar_net_interface_stat_get(sigar_t*sigar,constchar*name,
2. sigar_net_interface_stat_t*ifstat)
3. {
4. // 读取 /proc/net/dev
5. // 格式:
6. // Inter-| &nbsp; Receive &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp;Transmit
7. // &nbsp;face |bytes &nbsp; &nbsp;packets errs drop fifo frame compressed multicast|bytes &nbsp; &nbsp;packets errs drop fifo colls carrier compressed
8. // &nbsp; eth0: 123456 &nbsp; 1000 &nbsp; &nbsp;0 &nbsp; &nbsp;0 &nbsp; &nbsp;0 &nbsp; &nbsp; 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0 &nbsp; &nbsp; &nbsp; &nbsp; 654321 &nbsp; 2000 &nbsp; &nbsp;1 &nbsp; &nbsp;0 &nbsp; &nbsp;0 &nbsp; &nbsp; &nbsp;0 &nbsp; &nbsp; &nbsp; 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0

10. while(fgets(buffer,sizeof(buffer),&nbsp;fp)){
11. dev&nbsp;=&nbsp;buffer;
12. while(isspace(*dev))&nbsp;dev++;
13. if(!(ptr&nbsp;=&nbsp;strchr(dev,':')))continue;
14. *ptr++=0;

16. if(!strEQ(dev,&nbsp;name))continue;

18. // 接收统计
19. ifstat->rx_bytes &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);
20. ifstat->rx_packets &nbsp;=&nbsp;sigar_strtoull(ptr);
21. ifstat->rx_errors &nbsp;&nbsp;=&nbsp;sigar_strtoull(ptr);
22. ifstat->rx_dropped &nbsp;=&nbsp;sigar_strtoull(ptr);
23. ifstat->rx_overruns&nbsp;=&nbsp;sigar_strtoull(ptr);
24. ifstat->rx_frame &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);

26. ptr&nbsp;=&nbsp;sigar_skip_multiple_token(ptr,2);// 跳过 compressed, multicast

28. // 发送统计
29. ifstat->tx_bytes &nbsp; &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);
30. ifstat->tx_packets &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);
31. ifstat->tx_errors &nbsp; &nbsp;&nbsp;=&nbsp;sigar_strtoull(ptr);
32. ifstat->tx_dropped &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);
33. ifstat->tx_overruns &nbsp;&nbsp;=&nbsp;sigar_strtoull(ptr);
34. ifstat->tx_collisions&nbsp;=&nbsp;sigar_strtoull(ptr);
35. ifstat->tx_carrier &nbsp; &nbsp;=&nbsp;sigar_strtoull(ptr);

37. // 从 /sys/class/net/<name>/speed 获取网卡速度
38. ifstat->speed&nbsp;=&nbsp;nic_speed_get(name);

40. break;
41. }

43. return&nbsp;found&nbsp;?&nbsp;SIGAR_OK&nbsp;:&nbsp;ENXIO;
44. }

网卡速度 (linux_sigar.c:1915-1944)

1. static&nbsp;SIGAR_INLINE&nbsp;int&nbsp;nic_speed_get(constchar*name)
2. {
3. // 读取 /sys/class/net/<name>/speed
4. // 值为 Mbps

6. char&nbsp;buffer[BUFSIZ];
7. constchar*fname&nbsp;=&nbsp;nic_speed_filename(buffer,&nbsp;BUFSIZ,&nbsp;name);

9. if(fname){
10. FILE*fp&nbsp;=&nbsp;fopen(fname,"r");
11. if(fp){
12. int&nbsp;speed;
13. int&nbsp;n&nbsp;=&nbsp;fscanf(fp,"%d",&speed);
14. fclose(fp);
15. return&nbsp;n&nbsp;==1?&nbsp;speed&nbsp;:&nbsp;SIGAR_FIELD_NOTIMPL;
16. }
17. }

19. return&nbsp;SIGAR_FIELD_NOTIMPL;
20. }

网络连接 (linux_sigar.c:2099-2221)

1. staticint&nbsp;proc_net_read(sigar_net_connection_walker_t*walker,
2. constchar*fname,int&nbsp;type)
3. {
4. // 读取 /proc/net/tcp, /proc/net/tcp6, /proc/net/udp, /proc/net/udp6, /proc/net/raw
5. // 格式:
6. // &nbsp; sl &nbsp;local_address rem_address &nbsp; st tx_queue rx_queue tr tm->when retrnsmt &nbsp; uid &nbsp;timeout inode
7. // &nbsp; &nbsp;0: 0100007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 &nbsp; &nbsp; 0 &nbsp; &nbsp; &nbsp; &nbsp;0 12345 1 0000000000000000

9. // local_address 和 rem_address 为十六进制
10. // IPv4: 0100007F = 127.0.0.1, 0035 = 53 (端口)
11. // IPv6: 32 字符十六进制

13. while((ptr&nbsp;=&nbsp;fgets(buffer,sizeof(buffer),&nbsp;fp))){
14. // 跳过前导空格
15. SKIP_WHILE(ptr,' ');

17. // 跳过 "%d: "
18. SKIP_PAST(ptr,' ');

20. // 解析本地地址
21. laddr&nbsp;=&nbsp;ptr;
22. while(*ptr&nbsp;&&(*ptr&nbsp;!=':')){
23. laddr_len++;
24. ptr++;
25. }
26. SKIP_WHILE(ptr,':');
27. conn.local_port&nbsp;=(strtoul(ptr,&ptr,16)&0xffff);

29. // 解析远程地址
30. raddr&nbsp;=&nbsp;ptr;
31. while(*ptr&nbsp;&&(*ptr&nbsp;!=':')){
32. raddr_len++;
33. ptr++;
34. }
35. SKIP_WHILE(ptr,':');
36. conn.remote_port&nbsp;=(strtoul(ptr,&ptr,16)&0xffff);

38. // 转换地址
39. convert_hex_address(&conn.local_address,&nbsp;laddr,&nbsp;laddr_len);
40. convert_hex_address(&conn.remote_address,&nbsp;raddr,&nbsp;raddr_len);

42. // 解析状态 (2 位十六进制)
43. conn.state&nbsp;=&nbsp;hex2int(ptr,2);
44. ptr&nbsp;+=2;

46. // 解析队列
47. conn.send_queue&nbsp;=&nbsp;hex2int(ptr,&nbsp;HEX_ENT_LEN);
48. ptr&nbsp;+=&nbsp;HEX_ENT_LEN+1;
49. conn.receive_queue&nbsp;=&nbsp;hex2int(ptr,&nbsp;HEX_ENT_LEN);

51. // 解析 UID 和 inode
52. SKIP_PAST(ptr,' ');// tr:tm->whem
53. SKIP_PAST(ptr,' ');// retrnsmt
54. conn.uid&nbsp;=&nbsp;sigar_strtoul(ptr);
55. SKIP_WHILE(ptr,' ');
56. SKIP_PAST(ptr,' ');// timeout
57. conn.inode&nbsp;=&nbsp;sigar_strtoull(ptr);

59. more&nbsp;=&nbsp;walker->add_connection(walker,&conn);
60. if(more&nbsp;!=&nbsp;SIGAR_OK){
61. break;
62. }
63. }

65. return&nbsp;SIGAR_OK;
66. }

十六进制地址转换 (linux_sigar.c:2018-2035)

1. static&nbsp;SIGAR_INLINE&nbsp;void&nbsp;convert_hex_address(sigar_net_address_t*address,
2. char*ptr,int&nbsp;len)
3. {
4. if(len&nbsp;>&nbsp;HEX_ENT_LEN){// 8 字符以上为 IPv6
5. int&nbsp;i;
6. for(i=0;&nbsp;i<=3;&nbsp;i++,&nbsp;ptr+=HEX_ENT_LEN){
7. address->addr.in6[i]=&nbsp;hex2int(ptr,&nbsp;HEX_ENT_LEN);
8. }
9. address->family&nbsp;=&nbsp;SIGAR_AF_INET6;
10. }
11. else{// IPv4
12. address->addr.in&nbsp;=(len&nbsp;==&nbsp;HEX_ENT_LEN)?&nbsp;hex2int(ptr,&nbsp;HEX_ENT_LEN):0;
13. address->family&nbsp;=&nbsp;SIGAR_AF_INET;
14. }
15. }

路由表 (linux_sigar.c:1855-1913)

1. int&nbsp;sigar_net_route_list_get(sigar_t*sigar,sigar_net_route_list_t*routelist)
2. {
3. // 读取 /proc/net/route
4. // 格式:
5. // Iface &nbsp; &nbsp;Destination &nbsp; &nbsp; Gateway &nbsp; &nbsp; &nbsp; &nbsp; Flags &nbsp; &nbsp;RefCnt Use Metric Mask &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MTU Window IRTT
6. // eth0 &nbsp; &nbsp; 00000000 &nbsp; &nbsp; &nbsp; &nbsp;0101110A &nbsp; &nbsp; &nbsp; &nbsp;0003 &nbsp; &nbsp; 0 &nbsp; &nbsp; &nbsp;0 &nbsp; 0 &nbsp; &nbsp; &nbsp;00000000 &nbsp; &nbsp; &nbsp; &nbsp;1500 0 &nbsp; &nbsp; &nbsp;0

8. while(fgets(buffer,sizeof(buffer),&nbsp;fp)){
9. num&nbsp;=&nbsp;sscanf(buffer,&nbsp;ROUTE_FMT,
10. route->ifname,&nbsp;net_addr,&nbsp;gate_addr,
11. &flags,&route->refcnt,&route->use,
12. &route->metric,&nbsp;mask_addr,
13. &route->mtu,&route->window,&route->irtt);

15. if((num&nbsp;<10)||!(flags&nbsp;&&nbsp;RTF_UP)){// 必须是活动路由
16. --routelist->number;
17. continue;
18. }

20. route->flags&nbsp;=&nbsp;flags;

22. // 转换十六进制地址
23. sigar_net_address_set(route->destination,&nbsp;hex2int(net_addr,&nbsp;HEX_ENT_LEN));
24. sigar_net_address_set(route->gateway,&nbsp;hex2int(gate_addr,&nbsp;HEX_ENT_LEN));
25. sigar_net_address_set(route->mask,&nbsp;hex2int(mask_addr,&nbsp;HEX_ENT_LEN));
26. }

28. return&nbsp;SIGAR_OK;
29. }

IPv6 配置 (linux_sigar.c:2349-2389)

1. int&nbsp;sigar_net_interface_ipv6_config_get(sigar_t*sigar,constchar*name,
2. sigar_net_interface_config_t*ifconfig)
3. {
4. // 读取 /proc/net/if_inet6
5. // 格式:
6. // 00000000000000000000000000000001 02 40 00 00 &nbsp; eth0
7. // addr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;idx prefix scope flags ifname

9. while(fscanf(fp,"%32s %02x %02x %02x %02x %8s\n",
10. addr,&idx,&prefix,&scope,&flags,&nbsp;ifname)!=&nbsp;EOF)
11. {
12. if(strEQ(name,&nbsp;ifname)){
13. status&nbsp;=&nbsp;SIGAR_OK;
14. break;
15. }
16. }

18. if(status&nbsp;==&nbsp;SIGAR_OK){
19. // 转换 32 字符十六进制地址为 16 字节
20. unsignedchar*addr6&nbsp;=(unsignedchar*)&(ifconfig->address6.addr.in6);
21. char*ptr&nbsp;=&nbsp;addr;
22. for(i=0;&nbsp;i<16;&nbsp;i++,&nbsp;ptr+=2){
23. addr6[i]=(unsignedchar)hex2int(ptr,2);
24. }
25. ifconfig->prefix6_length&nbsp;=&nbsp;prefix;
26. ifconfig->scope6&nbsp;=&nbsp;scope;
27. }

29. return&nbsp;status;
30. }

ARP 表 (linux_sigar.c:2659-2742)

1. int&nbsp;sigar_arp_list_get(sigar_t*sigar,sigar_arp_list_t*arplist)
2. {
3. // 读取 /proc/net/arp
4. // 格式:
5. // IP address &nbsp; &nbsp; &nbsp; HW type &nbsp; &nbsp; Flags &nbsp; &nbsp; &nbsp; HW address &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Mask &nbsp; &nbsp; Device
6. // 192.168.1.1 &nbsp; &nbsp; 0x1 &nbsp; &nbsp; &nbsp; &nbsp; 0x2 &nbsp; &nbsp; &nbsp; &nbsp; 00:11:22:33:44:55 &nbsp; * &nbsp; &nbsp; &nbsp; &nbsp;eth0

8. while(fgets(buffer,sizeof(buffer),&nbsp;fp))){
9. num&nbsp;=&nbsp;sscanf(buffer,"%128s 0x%x 0x%x %128s %128s %16s",
10. net_addr,&type,&flags,
11. hwaddr,&nbsp;mask_addr,&nbsp;arp->ifname);

13. if(num&nbsp;<6){
14. --arplist->number;
15. continue;
16. }

18. arp->flags&nbsp;=&nbsp;flags;

20. // 解析 IP 地址 (支持 IPv4 和 IPv6)
21. status&nbsp;=&nbsp;inet_pton(AF_INET,&nbsp;net_addr,&arp->address.addr);
22. if(status&nbsp;>0){
23. arp->address.family&nbsp;=&nbsp;SIGAR_AF_INET;
24. }
25. elseif((status&nbsp;=&nbsp;inet_pton(AF_INET6,&nbsp;net_addr,&arp->address.addr))>0){
26. arp->address.family&nbsp;=&nbsp;SIGAR_AF_INET6;
27. }

29. // 解析 MAC 地址
30. num&nbsp;=&nbsp;sscanf(hwaddr,"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
31. &arp->hwaddr.addr.mac[0],&arp->hwaddr.addr.mac[1],
32. &arp->hwaddr.addr.mac[2],&arp->hwaddr.addr.mac[3],
33. &arp->hwaddr.addr.mac[4],&arp->hwaddr.addr.mac[5]);
34. if(num&nbsp;<6){
35. --arplist->number;
36. continue;
37. }
38. arp->hwaddr.family&nbsp;=&nbsp;SIGAR_AF_LINK;

40. // 硬件类型
41. SIGAR_SSTRCPY(arp->type,&nbsp;get_hw_type(type));
42. }

44. return&nbsp;SIGAR_OK;
45. }

TCP 统计 (linux_sigar.c:2420-2462)

1. int&nbsp;sigar_tcp_get(sigar_t*sigar,sigar_tcp_t*tcp)
2. {
3. // 读取 /proc/net/snmp
4. // 格式:
5. // Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts
6. // Tcp: 1 200 120000 -1 123 456 7 8 9 101112 131415 161718 19 202122

8. while(fgets(buffer,sizeof(buffer),&nbsp;fp)){
9. if(strnEQ(buffer,&nbsp;SNMP_TCP_PREFIX,sizeof(SNMP_TCP_PREFIX)-1)){
10. if(fgets(buffer,sizeof(buffer),&nbsp;fp)){
11. status&nbsp;=&nbsp;SIGAR_OK;
12. break;
13. }
14. }
15. }

17. if(status&nbsp;==&nbsp;SIGAR_OK){
18. ptr&nbsp;=&nbsp;sigar_skip_multiple_token(ptr,5);// 跳过前 5 个字段
19. tcp->active_opens&nbsp;=&nbsp;sigar_strtoull(ptr);
20. tcp->passive_opens&nbsp;=&nbsp;sigar_strtoull(ptr);
21. tcp->attempt_fails&nbsp;=&nbsp;sigar_strtoull(ptr);
22. tcp->estab_resets&nbsp;=&nbsp;sigar_strtoull(ptr);
23. tcp->curr_estab&nbsp;=&nbsp;sigar_strtoull(ptr);
24. tcp->in_segs&nbsp;=&nbsp;sigar_strtoull(ptr);
25. tcp->out_segs&nbsp;=&nbsp;sigar_strtoull(ptr);
26. tcp->retrans_segs&nbsp;=&nbsp;sigar_strtoull(ptr);
27. tcp->in_errs&nbsp;=&nbsp;sigar_strtoull(ptr);
28. tcp->out_rsts&nbsp;=&nbsp;sigar_strtoull(ptr);
29. }

31. return&nbsp;status;
32. }

端口到进程映射 (linux_sigar.c:2744-2852)

1. int&nbsp;sigar_proc_port_get(sigar_t*sigar,int&nbsp;protocol,
2. unsignedlong&nbsp;port,sigar_pid_t*pid)
3. {
4. // 1. 获取连接信息 (包含 inode)
5. status&nbsp;=&nbsp;sigar_net_connection_get(sigar,&netconn,&nbsp;port,
6. SIGAR_NETCONN_SERVER|protocol);

8. if(status&nbsp;!=&nbsp;SIGAR_OK){
9. return&nbsp;status;
10. }

12. // 2. 遍历 /proc/<pid>/fd,查找匹配的 inode
13. if(!(dirp&nbsp;=&nbsp;opendir(gPROCP_FS_ROOT))){
14. return&nbsp;errno;
15. }

17. while((ent&nbsp;=&nbsp;readdir(dirp))!=&nbsp;NULL){
18. if(!sigar_isdigit(*ent->d_name))continue;

20. // 构造路径: /proc/<pid>/fd
21. memcpy(&fd_name[0],&nbsp;fd_name,&nbsp;len);
22. memcpy(&fd_name[len],"/fd",3);
23. fd_name[len+=3]='\0';

25. if(!(fd_dirp&nbsp;=&nbsp;opendir(fd_name)))continue;

27. while((fd_ent&nbsp;=&nbsp;readdir(fd_dirp))!=&nbsp;NULL){
28. if(!sigar_isdigit(*fd_ent->d_name))continue;

30. // 构造路径: /proc/<pid>/fd/<fd>
31. // 检查 inode 是否匹配
32. if(stat(fd_ent_name,&sb)<0)continue;

34. if(sb.st_ino&nbsp;==&nbsp;netconn.inode){
35. closedir(fd_dirp);
36. closedir(dirp);
37. *pid&nbsp;=&nbsp;strtoul(ent->d_name,&nbsp;NULL,10);
38. return&nbsp;SIGAR_OK;
39. }
40. }
41. closedir(fd_dirp);
42. }

44. closedir(dirp);
45. return&nbsp;SIGAR_OK;
46. }

NFS 统计 (linux_sigar.c:2464-2599)

1. int&nbsp;sigar_nfs_client_v2_get(sigar_t*sigar,sigar_nfs_client_v2_t*nfs)
2. {
3. // 读取 /proc/net/rpc/nfs
4. // proc2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5. // 保留字段 followed by v2 操作计数

7. status&nbsp;=&nbsp;sigar_proc_nfs_gets(file,"proc2",&nbsp;buffer,sizeof(buffer));

9. ptr&nbsp;=&nbsp;sigar_skip_multiple_token(ptr,2);
10. nfs->null&nbsp;=&nbsp;sigar_strtoull(ptr);
11. nfs->getattr&nbsp;=&nbsp;sigar_strtoull(ptr);
12. nfs->setattr&nbsp;=&nbsp;sigar_strtoull(ptr);
13. // ... 其他操作

15. return&nbsp;SIGAR_OK;
16. }

5. 文件系统监控

文件系统列表 (linux_sigar.c:1227-1257)

1. int&nbsp;sigar_file_system_list_get(sigar_t*sigar,sigar_file_system_list_t*fslist)
2. {
3. // 读取 /etc/mtab (或 /proc/mounts)
4. // 格式:
5. // /dev/sda1 / ext4 rw,relatime,errors=remount-ro 0 0
6. // proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0

8. if(!(fp&nbsp;=&nbsp;setmntent(gMOUNTED,"r"))){
9. return&nbsp;errno;
10. }

12. while(getmntent_r(fp,&ent,&nbsp;buf,sizeof(buf))){
13. fsp&nbsp;=&fslist->data[fslist->number++];

15. fsp->type&nbsp;=&nbsp;SIGAR_FSTYPE_UNKNOWN;
16. SIGAR_SSTRCPY(fsp->dir_name,&nbsp;ent.mnt_dir);// 挂载点
17. SIGAR_SSTRCPY(fsp->dev_name,&nbsp;ent.mnt_fsname);// 设备名
18. SIGAR_SSTRCPY(fsp->sys_type_name,&nbsp;ent.mnt_type);// 文件系统类型
19. SIGAR_SSTRCPY(fsp->options,&nbsp;ent.mnt_opts);// 挂载选项

21. sigar_fs_type_get(fsp);// 根据 sys_type_name 设置 type
22. }

24. endmntent(fp);
25. return&nbsp;SIGAR_OK;
26. }

文件系统类型 (linux_sigar.c:1172-1225)

1. int&nbsp;sigar_os_fs_type_get(sigar_file_system_t*fsp)
2. {
3. char*type&nbsp;=&nbsp;fsp->sys_type_name;

5. switch(*type){
6. case'e':
7. if(strnEQ(type,"ext",3)){
8. fsp->type&nbsp;=&nbsp;SIGAR_FSTYPE_LOCAL_DISK;
9. }
10. break;
11. case'x':
12. if(strEQ(type,"xfs")||&nbsp;strEQ(type,"xiafs")){
13. fsp->type&nbsp;=&nbsp;SIGAR_FSTYPE_LOCAL_DISK;
14. }
15. break;
16. // ... 其他类型
17. }

19. return&nbsp;fsp->type;
20. }

文件系统使用情况 (linux_sigar.c:1613-1632)

1. int&nbsp;sigar_file_system_usage_get(sigar_t*sigar,
2. constchar*dirname,
3. sigar_file_system_usage_t*fsusage)
4. {
5. // 使用 statvfs() 获取使用情况
6. int&nbsp;status&nbsp;=&nbsp;sigar_statvfs(sigar,&nbsp;dirname,&nbsp;fsusage);

8. if(status&nbsp;!=&nbsp;SIGAR_OK){
9. return&nbsp;status;
10. }

12. fsusage->use_percent&nbsp;=&nbsp;sigar_file_system_usage_calc_used(sigar,&nbsp;fsusage);

14. // 获取磁盘 I/O 统计
15. (void)sigar_disk_usage_get(sigar,&nbsp;dirname,&fsusage->disk);

17. return&nbsp;SIGAR_OK;
18. }

6. 磁盘 I/O 监控

磁盘统计 (linux_sigar.c:1519-1611)

1. int&nbsp;sigar_disk_usage_get(sigar_t*sigar,constchar*name,
2. sigar_disk_usage_t*disk)
3. {
4. // 根据内核版本选择统计方法
5. switch(sigar->iostat){
6. case&nbsp;IOSTAT_SYS:
7. // 2.6 内核: /sys/block/<dev>/<dev><partition>/stat
8. status&nbsp;=&nbsp;get_iostat_sys(sigar,&nbsp;name,&nbsp;disk,&iodev);
9. break;
10. case&nbsp;IOSTAT_DISKSTATS:
11. // 2.6 内核: /proc/diskstats
12. status&nbsp;=&nbsp;get_iostat_proc_dstat(sigar,&nbsp;name,&nbsp;disk,&iodev,&device_usage);
13. break;
14. case&nbsp;IOSTAT_PARTITIONS:
15. // 2.4 内核: /proc/partitions
16. status&nbsp;=&nbsp;get_iostat_procp(sigar,&nbsp;name,&nbsp;disk,&iodev);
17. break;
18. case&nbsp;IOSTAT_NONE:
19. default:
20. status&nbsp;=&nbsp;ENOENT;
21. break;
22. }

24. // 计算派生指标
25. if((status&nbsp;==&nbsp;SIGAR_OK)&&&nbsp;iodev){
26. sigar_uptime_get(sigar,&uptime);

28. // 2.6 内核分区没有时间统计,使用整设备统计
29. if(iodev->is_partition&nbsp;&&(sigar->iostat&nbsp;==&nbsp;IOSTAT_DISKSTATS)){
30. partition_usage&nbsp;=&nbsp;disk;
31. disk&nbsp;=&device_usage;
32. }

34. disk->snaptime&nbsp;=&nbsp;uptime.uptime;

36. // 计算服务时间和队列长度
37. interval&nbsp;=&nbsp;disk->snaptime&nbsp;-&nbsp;iodev->disk.snaptime;
38. ios&nbsp;=(disk->reads&nbsp;-&nbsp;iodev->disk.reads)+
39. (disk->writes&nbsp;-&nbsp;iodev->disk.writes);

41. if(disk->time&nbsp;!=&nbsp;SIGAR_FIELD_NOTIMPL){
42. tput&nbsp;=((double)ios)*&nbsp;HZ&nbsp;/&nbsp;interval;
43. util&nbsp;=((double)(disk->time&nbsp;-&nbsp;iodev->disk.time))/&nbsp;interval&nbsp;*&nbsp;HZ;
44. disk->service_time&nbsp;=&nbsp;tput&nbsp;?&nbsp;util&nbsp;/&nbsp;tput&nbsp;:0.0;
45. }

47. if(disk->qtime&nbsp;!=&nbsp;SIGAR_FIELD_NOTIMPL){
48. util&nbsp;=((double)(disk->qtime&nbsp;-&nbsp;iodev->disk.qtime))/&nbsp;interval;
49. disk->queue=&nbsp;util&nbsp;/1000.0;
50. }
51. }

53. return&nbsp;status;
54. }

/proc/diskstats 方法 (linux_sigar.c:1322-1439)

1. staticint&nbsp;get_iostat_proc_dstat(sigar_t*sigar,
2. constchar*dirname,
3. sigar_disk_usage_t*disk,
4. sigar_iodev_t**iodev,
5. sigar_disk_usage_t*device_usage)
6. {
7. // 读取 /proc/diskstats
8. // 格式:
9. // &nbsp; major minor reads reads_merged reads_sectors reads_ms writes writes_merged writes_sectors writes_ms ios_in_progress ms_weighted_ios
10. // &nbsp; &nbsp;8 &nbsp; &nbsp;0 &nbsp; &nbsp;123 &nbsp;456 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 7890 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1234 &nbsp; 567 &nbsp;890 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;12345 &nbsp; &nbsp; &nbsp; &nbsp;6789 &nbsp; 10 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 12345

12. while((ptr&nbsp;=&nbsp;fgets(buffer,sizeof(buffer),&nbsp;fp))){
13. unsignedlong&nbsp;major,&nbsp;minor;

15. major&nbsp;=&nbsp;sigar_strtoul(ptr);
16. minor&nbsp;=&nbsp;sigar_strtoull(ptr);

18. if((major&nbsp;==&nbsp;ST_MAJOR(sb))&&
19. ((minor&nbsp;==&nbsp;ST_MINOR(sb))||(minor&nbsp;==0)))
20. {
21. ptr&nbsp;=&nbsp;sigar_skip_token(ptr);// name

23. num&nbsp;=&nbsp;sscanf(ptr,
24. "%lu %lu %lu %lu "// reads, reads_merged, reads_sectors, reads_ms
25. "%lu %lu %lu %lu "// writes, writes_merged, writes_sectors, writes_ms
26. "%lu %lu %lu",// ios_in_progress, ms_weighted_ios, ms_weighted_ios(aveq)
27. &rio,&rmerge,&rsect,&ruse,
28. &wio,&wmerge,&wsect,&wuse,
29. &running,&use,&aveq);

31. if(num&nbsp;==11){// 完整格式
32. disk->rtime&nbsp;=&nbsp;ruse;
33. disk->wtime&nbsp;=&nbsp;wuse;
34. disk->time&nbsp;=&nbsp;use;
35. disk->qtime&nbsp;=&nbsp;aveq;
36. disk->ios&nbsp;=&nbsp;running;
37. }
38. elseif(num&nbsp;==4){// 旧格式
39. wio&nbsp;=&nbsp;rsect;
40. rsect&nbsp;=&nbsp;rmerge;
41. wsect&nbsp;=&nbsp;ruse;
42. disk->time&nbsp;=&nbsp;disk->qtime&nbsp;=&nbsp;SIGAR_FIELD_NOTIMPL;
43. }

45. disk->reads&nbsp;=&nbsp;rio;
46. disk->writes&nbsp;=&nbsp;wio;
47. disk->read_bytes &nbsp;=&nbsp;rsect;
48. disk->write_bytes&nbsp;=&nbsp;wsect;

50. // 转换扇区为字节 (512 字节/扇区)
51. disk->read_bytes &nbsp;*=512;
52. disk->write_bytes&nbsp;*=512;

54. break;
55. }
56. }

58. return&nbsp;status;
59. }

7. 系统信息

发行版信息 (linux_sigar.c:3017-3079)

1. staticint&nbsp;get_linux_vendor_info(sigar_sys_info_t*info)
2. {
3. // 支持的发行版:
4. // - Fedora: &nbsp; &nbsp; &nbsp;/etc/fedora-release
5. // - SuSE: &nbsp; &nbsp; &nbsp; &nbsp;/etc/SuSE-release
6. // - Gentoo: &nbsp; &nbsp; &nbsp;/etc/gentoo-release
7. // - Slackware: &nbsp; /etc/slackware-version
8. // - Mandrake: &nbsp; &nbsp;/etc/mandrake-release
9. // - VMware: &nbsp; &nbsp; &nbsp;/proc/vmware/version
10. // - XenSource: &nbsp; /etc/xensource-inventory
11. // - Oracle: &nbsp; &nbsp; &nbsp;/etc/oracle-release
12. // - Red Hat: &nbsp; &nbsp; /etc/redhat-release
13. // - LSB: &nbsp; &nbsp; &nbsp; &nbsp; /etc/lsb-release
14. // - Debian: &nbsp; &nbsp; &nbsp;/etc/debian_version

16. linux_vendor_info_t&nbsp;linux_vendors[]={
17. {"Fedora","/etc/fedora-release",&nbsp;NULL&nbsp;},
18. {"SuSE","/etc/SuSE-release",&nbsp;NULL&nbsp;},
19. {"Gentoo","/etc/gentoo-release",&nbsp;NULL&nbsp;},
20. {"Red Hat","/etc/redhat-release",&nbsp;redhat_vendor_parse&nbsp;},
21. {"lsb","/etc/lsb-release",&nbsp;lsb_vendor_parse&nbsp;},
22. {"Debian","/etc/debian_version",&nbsp;NULL&nbsp;},
23. {&nbsp;NULL&nbsp;}
24. };

26. for(i=0;&nbsp;linux_vendors[i].name;&nbsp;i++){
27. vendor&nbsp;=&linux_vendors[i];

29. if(stat(vendor->file,&sb)<0){
30. continue;
31. }

33. status&nbsp;=&nbsp;sigar_file2str(vendor->file,&nbsp;buffer,sizeof(buffer)-1);
34. break;
35. }

37. SIGAR_SSTRCPY(info->vendor,&nbsp;vendor->name);

39. if(vendor->parse){
40. vendor->parse(data,&nbsp;info);
41. }
42. else{
43. generic_vendor_parse(data,&nbsp;info);
44. }

46. return&nbsp;SIGAR_OK;
47. }

系统启动时间 (linux_sigar.c:146-174)

1. staticint&nbsp;sigar_boot_time_get(sigar_t*sigar)
2. {
3. FILE*fp;
4. char&nbsp;buffer[BUFSIZ],*ptr;
5. int&nbsp;found&nbsp;=0;

7. if(!(fp&nbsp;=&nbsp;fopen(gPROC_STAT,"r"))){
8. return&nbsp;errno;
9. }

11. // 读取 /proc/stat 中的 btime 字段
12. while((ptr&nbsp;=&nbsp;fgets(buffer,sizeof(buffer),&nbsp;fp))){
13. if(strnEQ(ptr,"btime",5)){
14. if((ptr&nbsp;=&nbsp;sigar_skip_token(ptr))){
15. sigar->boot_time&nbsp;=&nbsp;sigar_strtoul(ptr);
16. found&nbsp;=1;
17. }
18. break;
19. }
20. }

22. fclose(fp);

24. if(!found){
25. sigar->boot_time&nbsp;=&nbsp;time(NULL);// 回退到当前时间
26. }

28. return&nbsp;SIGAR_OK;
29. }

系统运行时间 (linux_sigar.c:519-532)

1. int&nbsp;sigar_uptime_get(sigar_t*sigar,sigar_uptime_t*uptime)
2. {
3. // 读取 /proc/uptime
4. // 格式: <uptime_seconds> <idle_seconds>

6. status&nbsp;=&nbsp;sigar_file2str(gPROC_UPTIME,&nbsp;buffer,sizeof(buffer));

8. uptime->uptime&nbsp;=&nbsp;strtod(buffer,&ptr);

10. return&nbsp;SIGAR_OK;
11. }

系统负载 (linux_sigar.c:534-551)

1. int&nbsp;sigar_loadavg_get(sigar_t*sigar,sigar_loadavg_t*loadavg)
2. {
3. // 读取 /proc/loadavg
4. // 格式: <load1> <load5> <load15> <running_tasks> <total_tasks> <last_pid>

6. status&nbsp;=&nbsp;sigar_file2str(gPROC_LOADAVG,&nbsp;buffer,sizeof(buffer));

8. loadavg->loadavg[0]=&nbsp;strtod(buffer,&ptr);// 1 分钟平均
9. loadavg->loadavg[1]=&nbsp;strtod(ptr,&ptr);// 5 分钟平均
10. loadavg->loadavg[2]=&nbsp;strtod(ptr,&ptr);// 15 分钟平均
11. loadavg->processor_queue&nbsp;=&nbsp;strtod(ptr,&ptr);// 进程队列长度

13. return&nbsp;SIGAR_OK;
14. }

系统 UUID (linux_sigar.c:3081-3118)

1. int&nbsp;sigar_sys_info_get_uuid(sigar_t*sigar,char&nbsp;uuid[SIGAR_SYS_INFO_LEN])
2. {
3. // 尝试从 /etc/machine-id 读取 (systemd 系统)
4. FILE*fp&nbsp;=&nbsp;fopen("/etc/machine-id","r");
5. if(fp){
6. if(fgets(uuid,&nbsp;SIGAR_SYS_INFO_LEN&nbsp;-1,&nbsp;fp)){
7. found&nbsp;=1;
8. }
9. fclose(fp);
10. }
11. else{
12. // 回退到磁盘 UUID
13. DIR*ctx&nbsp;=&nbsp;opendir("/dev/disk/by-uuid");
14. if(ctx){
15. while((data&nbsp;=&nbsp;readdir(ctx))!=&nbsp;NULL){
16. constchar*fs_uuid&nbsp;=&nbsp;data->d_name;
17. if(strlen(data->d_name)==36||&nbsp;strlen(data->d_name)==22){
18. strncat(uuid,&nbsp;fs_uuid,&nbsp;SIGAR_SYS_INFO_LEN&nbsp;-&nbsp;strlen(fs_uuid)-1);
19. found&nbsp;=1;
20. break;
21. }
22. }
23. closedir(ctx);
24. }
25. }

27. return&nbsp;found&nbsp;?&nbsp;SIGAR_OK&nbsp;:&nbsp;SIGAR_ENOTIMPL;
28. }

容器检测 (linux_sigar.c:3135-3164)

1. int&nbsp;sigar_os_is_in_container(sigar_t*sigar)
2. {
3. // 检查 /proc/1/cgroup
4. // 非容器: 3:cpu:/
5. // 容器: &nbsp; &nbsp;3:cpu:/docker/58db180b111f8cbc4c08cb58c162c740c728c398cc54bec4586e38d058a028d6

7. snprintf(buffer,sizeof(buffer),"%s/1/cgroup",&nbsp;PROC_FS_ROOT);

9. if(!(fp&nbsp;=&nbsp;fopen(buffer,"r"))){
10. goto&nbsp;out;
11. }

13. while((ptr&nbsp;=&nbsp;fgets(buffer,sizeof(buffer),&nbsp;fp))){
14. // OpenSUSE hack: 跳过包含 "=" 的行 (如 1:name=systemd:/system)
15. tmp_ptr&nbsp;=&nbsp;strstr(buffer,"=");
16. if(tmp_ptr){
17. continue;
18. }

20. tmp_ptr&nbsp;=&nbsp;strstr(buffer,&nbsp;DOCKER_KEY_STR);// ":/docker/"
21. if(tmp_ptr){
22. in_container&nbsp;=1;
23. break;
24. }
25. }

27. fclose(fp);

29. out:
30. return&nbsp;in_container;
31. }

性能优化

1. 进程信息缓存 (linux_sigar.c:718-726)

1. time_t&nbsp;timenow&nbsp;=&nbsp;time(NULL);

3. if(pstat->pid&nbsp;==&nbsp;pid){
4. if((timenow&nbsp;-&nbsp;pstat->mtime)<&nbsp;SIGAR_LAST_PROC_EXPIRE){
5. return&nbsp;SIGAR_OK;// 60 秒内复用缓存
6. }
7. }

9. pstat->pid&nbsp;=&nbsp;pid;
10. pstat->mtime&nbsp;=&nbsp;timenow;

2. 动态缓冲区管理

使用固定大小的栈缓冲区 ( BUFSIZ) 读取 proc 文件,避免频繁内存分配。

3. 字符串解析优化

1. // 跳过标记的宏
2. #define&nbsp;SIGAR_SKIP_SPACE(ptr)while(isspace(*ptr))++ptr
3. #define&nbsp;sigar_skip_token(ptr)(SIGAR_SKIP_SPACE(ptr),*ptr++)

5. // 跳过多个标记
6. ptr&nbsp;=&nbsp;sigar_skip_multiple_token(ptr,4);

4. 延迟初始化

磁盘 I/O 统计方法在初始化时通过 stat() 检测,避免运行时重复检查。

5. 多级回退机制

  • 内存信息: /proc/meminfo → /proc/mtrr
  • 磁盘统计: /proc/diskstats → /sys/block → /proc/partitions
  • 系统 UUID: /etc/machine-id → /dev/disk/by-uuid

特殊技术

1. 页面位移计算 (linux_sigar.c:36)

1. #define&nbsp;pageshift(x)((x)<<&nbsp;sigar->pagesize)

将页数转换为字节数,避免乘法运算。

2. 十六进制转换 (linux_sigar.c:1824-1844)

1. static&nbsp;SIGAR_INLINE&nbsp;unsignedint&nbsp;hex2int(constchar*x,int&nbsp;len)
2. {
3. int&nbsp;i;
4. unsignedint&nbsp;j;

6. for(i=0,&nbsp;j=0;&nbsp;i<len;&nbsp;i++){
7. registerint&nbsp;ch&nbsp;=&nbsp;x[i];
8. j&nbsp;<<=4;
9. if(isdigit(ch)){
10. j&nbsp;|=&nbsp;ch&nbsp;-'0';
11. }
12. elseif(isupper(ch)){
13. j&nbsp;|=&nbsp;ch&nbsp;-('A'-10);
14. }
15. else{
16. j&nbsp;|=&nbsp;ch&nbsp;-('a'-10);
17. }
18. }

20. return&nbsp;j;
21. }

3. 进程名解析 (linux_sigar.c:958-971)

1. // /proc/<pid>/stat 中的进程名可能被截断为 15 字符
2. if(strlen(pstat->name)==15){
3. if(sigar_procfs_args_get(sigar,&nbsp;pid,&procargs)==&nbsp;SIGAR_OK&nbsp;&&
4. procargs.number&nbsp;>=1){
5. procname&nbsp;=&nbsp;procargs.data[0];// 从命令行获取完整名称
6. }
7. }

4. CPU 核心合并 (linux_sigar.c:492-500)

1. if(core_rollup&nbsp;&&(i&nbsp;%&nbsp;sigar->lcpu)){
2. // 合并超线程逻辑处理器
3. cpu&nbsp;=&cpulist->data[cpulist->number-1];
4. }
5. else{
6. // 创建新的物理核心条目
7. SIGAR_CPU_LIST_GROW(cpulist);
8. cpu&nbsp;=&cpulist->data[cpulist->number++];
9. }

5. 环境变量解析 (linux_sigar.c:1039-1064)

1. while(ptr&nbsp;<&nbsp;end){
2. char*val&nbsp;=&nbsp;strchr(ptr,'=');

4. if(val&nbsp;==&nbsp;NULL){
5. break;
6. }

8. klen&nbsp;=&nbsp;val&nbsp;-&nbsp;ptr;
9. SIGAR_SSTRCPY(key,&nbsp;ptr);
10. key[klen]='\0';
11. ++val;

13. vlen&nbsp;=&nbsp;strlen(val);
14. status&nbsp;=&nbsp;procenv->env_getter(procenv->data,&nbsp;key,&nbsp;klen,&nbsp;val,&nbsp;vlen);

16. if(status&nbsp;!=&nbsp;SIGAR_OK){
17. break;
18. }

20. ptr&nbsp;+=(klen&nbsp;+1+&nbsp;vlen&nbsp;+1);// key= + value + \0
21. }

内核版本兼容性

1. Linux 2.2

  • /proc/stat 包含磁盘统计
  • 简化的 CPU 统计
  • 无 /proc/diskstats

2. Linux 2.4

  • /proc/partitions 包含磁盘统计
  • 增强的 CPU 统计
  • 无 /proc/diskstats

3. Linux 2.6

  • /proc/diskstats 提供详细磁盘统计
  • /sys/block 提供设备级统计
  • 完整的 CPU 统计 (包括 iowait, irq, softirq, steal)
  • /proc/<pid>/io 提供进程磁盘 I/O
  • NPTL 支持

4. Linux 2.6.11+

  • 新增 steal 字段 (虚拟化 CPU 时间)

5. Linux 2.6.20+

  • /proc/<pid>/io 提供 I/O 统计

6. Linux 3.x+

  • 改进的调度器统计
  • 更多的 proc 文件

技术亮点

1. 统一的 procfs 接口

Linux 提供了 /proc 虚拟文件系统,所有系统信息都通过文件访问获取,无需调用特殊的系统 API。

2. 文本格式解析

所有信息都是纯文本格式,易于解析和调试。

3. 零拷贝设计

通过直接读取文件内容,避免了额外的系统调用和数据复制。

4. 自适应内核版本

根据内核版本和文件可用性自动选择最佳的信息源。

5. 高效的缓存机制

进程信息缓存减少了重复的文件读取。

6. 便携性

相同代码适用于各种 Linux 发行版和内核版本。

限制和挑战

1. 文件格式变化

不同内核版本的 proc 文件格式可能变化,需要维护兼容性。

2. 性能开销

频繁读取 proc 文件可能有性能开销,特别是在高频率监控时。

3. 权限要求

某些信息需要 root 权限才能访问 (如其他用户的进程环境变量)。

4. 线程检测

旧内核的线程模型复杂,需要通过 hack 检测线程。

5. 容器环境

容器环境中某些信息可能不准确或不可用。

总结

libsigar Linux 实现充分利用了 Linux 的 /proc 和 /sys 文件系统,提供了一个统一、高效、跨发行版的系统信息采集接口。其设计体现了以下特点:

  1. 简洁性: 通过文件系统接口获取信息,代码简洁易懂
  2. 高效性: 使用缓存和优化解析减少开销
  3. 兼容性: 支持从 2.2 到 3.x 的各种内核版本
  4. 可扩展性: 模块化设计,易于添加新功能
  5. 健壮性: 多级回退机制确保在各种环境都能工作

这种实现方式充分展示了 Linux 内核设计的优势,也为其他平台的系统监控提供了参考。

  • 公众号:安全狗的自我修养
  • vx:2207344074
  • http://gitee.com/haidragon
  • http://github.com/haidragon
  • bilibili:haidragonx

#

#


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:安全狗的自我修养 haidragon haidragon《跨平台系统信息获取库libsigar源码分析系列(一)》

三维软件许可优化技术 网络安全文章

三维软件许可优化技术

文章总结: 文档介绍德斯克许可优化技术,旨在解决企业正版软件授权不足与采购成本高昂的痛点。该技术声称通过许可管理器端口介入,实现许可自动回收与复用,以少量授权满
评论:0   参与:  0