文章总结: 该文档详细分析了跨平台库libsigar在Linux平台的源码实现,核心在于利用/proc和/sys文件系统获取系统信息。文章解析了关键数据结构sigar_t与linux_proc_stat_t,阐述了初始化流程及磁盘I/O统计方法检测机制。重点探讨了内存、CPU及进程监控模块的实现细节,如通过/proc/meminfo获取内存状态、解析/proc/stat计算CPU使用率以及读取/proc下的进程状态文件。内容为理解系统底层监控技术与安全工具依赖库原理提供了详尽参考。 综合评分: 88 文章分类: 代码审计,安全开发,安全工具
跨平台系统信息获取库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 >0)&& isdigit(buffer[n--]));// 跳过字段
14. while((n >0)&&!isdigit(buffer[n--]));// 跳过空格
15. }
17. ptr =&buffer[n];
18. if((*ptr++=='1')&&(*ptr++=='7')&&(*ptr++==' ')){
19. return0;// 真正的进程
20. }
22. return1;// 线程
23. }
进程状态读取 (linux_sigar.c:709-808)
1. staticint proc_stat_read(sigar_t*sigar,sigar_pid_t pid)
2. {
3. // 短期缓存: 60 秒内重复读取同一进程使用缓存
4. if(pstat->pid == pid){
5. if((timenow - pstat->mtime)< SIGAR_LAST_PROC_EXPIRE){
6. return SIGAR_OK;
7. }
8. }
10. // 读取 /proc/<pid>/stat
11. // 格式 (部分字段):
12. // 1 pid %d
13. // 2 comm %s (进程名,可能被截断)
14. // 3 state %c
15. // 4 ppid %d
16. // 14 utime %lu (用户时间,时钟周期)
17. // 15 stime %lu (系统时间,时钟周期)
18. // 22 starttime %lu (启动后的时钟周期数)
19. // 23 vsize %lu (虚拟内存大小)
20. // 24 rss %lu (常驻集大小,页数)
22. // 解析进程名 (处理括号内的可能包含空格的进程名)
23. if(!(ptr = strchr(ptr,'('))){
24. return EINVAL;
25. }
26. if(!(tmp = strrchr(++ptr,')'))){
27. return EINVAL;
28. }
29. len = tmp-ptr;
30. memcpy(pstat->name, ptr, len);
31. pstat->name[len]='\0';
33. // 跳过其他字段...
34. // 解析关键信息
36. pstat->utime = SIGAR_TICK2MSEC(sigar_strtoull(ptr));// 用户时间 (毫秒)
37. pstat->stime = SIGAR_TICK2MSEC(sigar_strtoull(ptr));// 系统时间 (毫秒)
39. pstat->start_time = sigar_strtoul(ptr);// 时钟周期数
40. pstat->start_time /= sigar->ticks;// 转换为秒
41. pstat->start_time += sigar->boot_time;// 加上启动时间
42. pstat->start_time *=1000;// 转换为毫秒
44. pstat->vsize = sigar_strtoull(ptr);// 虚拟内存 (字节)
45. pstat->rss = pageshift(sigar_strtoull(ptr));// 常驻集 (字节)
47. return SIGAR_OK;
48. }
进程内存 (linux_sigar.c:810-833)
1. int sigar_proc_mem_get(sigar_t*sigar,sigar_pid_t pid,sigar_proc_mem_t*procmem)
2. {
3. // 从缓存的 stat 读取缺页错误
4. procmem->minor_faults = pstat->minor_faults;
5. procmem->major_faults = pstat->major_faults;
6. procmem->page_faults = procmem->minor_faults + procmem->major_faults;
8. // 读取 /proc/<pid>/statm
9. // 格式: size resident shared text lib data dt
10. // size: 总虚拟内存大小 (页)
11. // resident: 常驻集大小 (页)
12. // shared: 共享内存页数
14. status = SIGAR_PROC_FILE2STR(buffer, pid,"/statm");
16. procmem->size = pageshift(sigar_strtoull(ptr));
17. procmem->resident = pageshift(sigar_strtoull(ptr));
18. procmem->share = pageshift(sigar_strtoull(ptr));
20. return SIGAR_OK;
21. }
进程磁盘 I/O (linux_sigar.c:845-861)
1. int sigar_proc_cumulative_disk_io_get(sigar_t*sigar,sigar_pid_t pid,
2. sigar_proc_cumulative_disk_io_t*proc_cumulative_disk_io)
3. {
4. // 读取 /proc/<pid>/io (2.6.20+)
5. // read_bytes: 读字节数
6. // write_bytes: 写字节数
7. // cancel_write_bytes: 取消写回的字节数
9. status = SIGAR_PROC_FILE2STR(buffer, pid,"/io");
11. proc_cumulative_disk_io->bytes_read = get_named_proc_token(buffer,"\nread_bytes");
12. proc_cumulative_disk_io->bytes_written = get_named_proc_token(buffer,"\nwrite_bytes");
13. proc_cumulative_disk_io->bytes_total =
14. proc_cumulative_disk_io->bytes_read + proc_cumulative_disk_io->bytes_written;
16. return SIGAR_OK;
17. }
进程凭据 (linux_sigar.c:865-900)
1. int sigar_proc_cred_get(sigar_t*sigar,sigar_pid_t 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 = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS);
9. if((ptr = strstr(buffer,"\nUid:"))){
10. ptr = sigar_skip_token(ptr);
11. proccred->uid = sigar_strtoul(ptr);// 真实 UID
12. proccred->euid = sigar_strtoull(ptr);// 有效 UID
13. }
15. if((ptr = strstr(ptr,"\nGid:"))){
16. ptr = sigar_skip_token(ptr);
17. proccred->gid = sigar_strtoul(ptr);// 真实 GID
18. proccred->egid = sigar_strtoull(ptr);// 有效 GID
19. }
21. return SIGAR_OK;
22. }
进程状态 (linux_sigar.c:943-997)
1. int sigar_proc_state_get(sigar_t*sigar,sigar_pid_t pid,sigar_proc_state_t*procstate)
2. {
3. // 从 stat 读取基本状态
4. procstate->state = pstat->state;// R|S|D|Z|T
5. procstate->ppid = pstat->ppid;
6. procstate->tty = pstat->tty;
7. procstate->priority = pstat->priority;
8. procstate->nice = pstat->nice;
9. procstate->processor = pstat->processor;
11. // 进程名处理: 如果被截断为 15 字符,尝试从命令行获取完整名称
12. if(strlen(pstat->name)==15){
13. if(sigar_procfs_args_get(sigar, pid,&procargs)== SIGAR_OK &&
14. procargs.number >=1){
15. procname = procargs.data[0];
16. }
17. }
19. // 从 status 读取线程数
20. ptr = strstr(buffer,"\nThreads:");
21. if(ptr){
22. ptr = sigar_skip_token(ptr);
23. procstate->threads = sigar_strtoul(ptr);
24. }
26. // 获取打开的文件描述符数量
27. sigar_proc_fd_t proc_fd_count;
28. if(sigar_proc_fd_get(sigar, pid,&proc_fd_count)== SIGAR_OK){
29. procstate->open_files = proc_fd_count.total;
30. }
32. return SIGAR_OK;
33. }
进程环境变量 (linux_sigar.c:1010-1067)
1. int sigar_proc_env_get(sigar_t*sigar,sigar_pid_t pid,sigar_proc_env_t*procenv)
2. {
3. // 读取 /proc/<pid>/environ
4. // 格式: key1=value1\0key2=value2\0...\0
6. if((fd = open(name, O_RDONLY))<0){
7. if(errno == ENOENT){
8. return ESRCH;// 进程不存在
9. }
10. return errno;
11. }
13. len = read(fd, buffer,sizeof(buffer));
14. close(fd);
16. buffer[len]='\0';
17. ptr = buffer;
19. end = buffer + len;
20. while(ptr < end){
21. char*val = strchr(ptr,'=');
23. if(val == NULL){
24. break;
25. }
27. klen = val - ptr;
28. SIGAR_SSTRCPY(key, ptr);
29. key[klen]='\0';
30. ++val;
32. vlen = strlen(val);
33. status = procenv->env_getter(procenv->data, key, klen, val, vlen);
35. if(status != SIGAR_OK){
36. break;// 回调要求停止
37. }
39. ptr +=(klen +1+ vlen +1);
40. }
42. return SIGAR_OK;
43. }
进程可执行文件信息 (linux_sigar.c:1078-1107)
1. int sigar_proc_exe_get(sigar_t*sigar,sigar_pid_t 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, pid,"/cwd");
8. if((len = readlink(name, procexe->cwd,sizeof(procexe->cwd)-1))>=0){
9. procexe->cwd[len]='\0';
10. }
12. (void)SIGAR_PROC_FILENAME(name, pid,"/exe");
13. if((len = readlink(name, procexe->name,sizeof(procexe->name)-1))>=0){
14. procexe->name[len]='\0';
15. }
17. (void)SIGAR_PROC_FILENAME(name, pid,"/root");
18. if((len = readlink(name, procexe->root,sizeof(procexe->root)-1))>=0){
19. procexe->root[len]='\0';
20. }
22. // 通过 ELF 文件猜测架构
23. procexe->arch = sigar_elf_file_guess_arch(sigar, procexe->name);
25. return SIGAR_OK;
26. }
进程模块 (linux_sigar.c:1109-1151)
1. int sigar_proc_modules_get(sigar_t*sigar,sigar_pid_t pid,sigar_proc_modules_t*procmods)
2. {
3. // 读取 /proc/<pid>/maps
4. // 格式:
5. // address perms offset dev inode 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 = fgets(buffer,sizeof(buffer), fp))){
10. // 跳过前 4 个字段 (address, perms, offset, dev)
11. ptr = sigar_skip_multiple_token(ptr,4);
12. inode = sigar_strtoul(ptr);
14. // inode 为 0 表示匿名映射,跳过
15. if((inode ==0)||(inode == last_inode)){
16. last_inode =0;
17. continue;
18. }
20. last_inode = inode;
21. ptr = strrchr(ptr,' ')+1;// 路径名在最后
22. len = strlen(ptr);
23. ptr[len-1]='\0';// 去掉换行符
25. status = procmods->module_getter(procmods->data, ptr, len-1);
27. if(status != SIGAR_OK){
28. break;
29. }
30. }
32. return SIGAR_OK;
33. }
4. 网络监控
网络接口统计 (linux_sigar.c:1946-2016)
1. int 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-| Receive | Transmit
7. // face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
8. // eth0: 123456 1000 0 0 0 0 0 654321 2000 1 0 0 0 0 0
10. while(fgets(buffer,sizeof(buffer), fp)){
11. dev = buffer;
12. while(isspace(*dev)) dev++;
13. if(!(ptr = strchr(dev,':')))continue;
14. *ptr++=0;
16. if(!strEQ(dev, name))continue;
18. // 接收统计
19. ifstat->rx_bytes = sigar_strtoull(ptr);
20. ifstat->rx_packets = sigar_strtoull(ptr);
21. ifstat->rx_errors = sigar_strtoull(ptr);
22. ifstat->rx_dropped = sigar_strtoull(ptr);
23. ifstat->rx_overruns = sigar_strtoull(ptr);
24. ifstat->rx_frame = sigar_strtoull(ptr);
26. ptr = sigar_skip_multiple_token(ptr,2);// 跳过 compressed, multicast
28. // 发送统计
29. ifstat->tx_bytes = sigar_strtoull(ptr);
30. ifstat->tx_packets = sigar_strtoull(ptr);
31. ifstat->tx_errors = sigar_strtoull(ptr);
32. ifstat->tx_dropped = sigar_strtoull(ptr);
33. ifstat->tx_overruns = sigar_strtoull(ptr);
34. ifstat->tx_collisions = sigar_strtoull(ptr);
35. ifstat->tx_carrier = sigar_strtoull(ptr);
37. // 从 /sys/class/net/<name>/speed 获取网卡速度
38. ifstat->speed = nic_speed_get(name);
40. break;
41. }
43. return found ? SIGAR_OK : ENXIO;
44. }
网卡速度 (linux_sigar.c:1915-1944)
1. static SIGAR_INLINE int nic_speed_get(constchar*name)
2. {
3. // 读取 /sys/class/net/<name>/speed
4. // 值为 Mbps
6. char buffer[BUFSIZ];
7. constchar*fname = nic_speed_filename(buffer, BUFSIZ, name);
9. if(fname){
10. FILE*fp = fopen(fname,"r");
11. if(fp){
12. int speed;
13. int n = fscanf(fp,"%d",&speed);
14. fclose(fp);
15. return n ==1? speed : SIGAR_FIELD_NOTIMPL;
16. }
17. }
19. return SIGAR_FIELD_NOTIMPL;
20. }
网络连接 (linux_sigar.c:2099-2221)
1. staticint proc_net_read(sigar_net_connection_walker_t*walker,
2. constchar*fname,int type)
3. {
4. // 读取 /proc/net/tcp, /proc/net/tcp6, /proc/net/udp, /proc/net/udp6, /proc/net/raw
5. // 格式:
6. // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
7. // 0: 0100007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 12345 1 0000000000000000
9. // local_address 和 rem_address 为十六进制
10. // IPv4: 0100007F = 127.0.0.1, 0035 = 53 (端口)
11. // IPv6: 32 字符十六进制
13. while((ptr = fgets(buffer,sizeof(buffer), fp))){
14. // 跳过前导空格
15. SKIP_WHILE(ptr,' ');
17. // 跳过 "%d: "
18. SKIP_PAST(ptr,' ');
20. // 解析本地地址
21. laddr = ptr;
22. while(*ptr &&(*ptr !=':')){
23. laddr_len++;
24. ptr++;
25. }
26. SKIP_WHILE(ptr,':');
27. conn.local_port =(strtoul(ptr,&ptr,16)&0xffff);
29. // 解析远程地址
30. raddr = ptr;
31. while(*ptr &&(*ptr !=':')){
32. raddr_len++;
33. ptr++;
34. }
35. SKIP_WHILE(ptr,':');
36. conn.remote_port =(strtoul(ptr,&ptr,16)&0xffff);
38. // 转换地址
39. convert_hex_address(&conn.local_address, laddr, laddr_len);
40. convert_hex_address(&conn.remote_address, raddr, raddr_len);
42. // 解析状态 (2 位十六进制)
43. conn.state = hex2int(ptr,2);
44. ptr +=2;
46. // 解析队列
47. conn.send_queue = hex2int(ptr, HEX_ENT_LEN);
48. ptr += HEX_ENT_LEN+1;
49. conn.receive_queue = hex2int(ptr, HEX_ENT_LEN);
51. // 解析 UID 和 inode
52. SKIP_PAST(ptr,' ');// tr:tm->whem
53. SKIP_PAST(ptr,' ');// retrnsmt
54. conn.uid = sigar_strtoul(ptr);
55. SKIP_WHILE(ptr,' ');
56. SKIP_PAST(ptr,' ');// timeout
57. conn.inode = sigar_strtoull(ptr);
59. more = walker->add_connection(walker,&conn);
60. if(more != SIGAR_OK){
61. break;
62. }
63. }
65. return SIGAR_OK;
66. }
十六进制地址转换 (linux_sigar.c:2018-2035)
1. static SIGAR_INLINE void convert_hex_address(sigar_net_address_t*address,
2. char*ptr,int len)
3. {
4. if(len > HEX_ENT_LEN){// 8 字符以上为 IPv6
5. int i;
6. for(i=0; i<=3; i++, ptr+=HEX_ENT_LEN){
7. address->addr.in6[i]= hex2int(ptr, HEX_ENT_LEN);
8. }
9. address->family = SIGAR_AF_INET6;
10. }
11. else{// IPv4
12. address->addr.in =(len == HEX_ENT_LEN)? hex2int(ptr, HEX_ENT_LEN):0;
13. address->family = SIGAR_AF_INET;
14. }
15. }
路由表 (linux_sigar.c:1855-1913)
1. int sigar_net_route_list_get(sigar_t*sigar,sigar_net_route_list_t*routelist)
2. {
3. // 读取 /proc/net/route
4. // 格式:
5. // Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
6. // eth0 00000000 0101110A 0003 0 0 0 00000000 1500 0 0
8. while(fgets(buffer,sizeof(buffer), fp)){
9. num = sscanf(buffer, ROUTE_FMT,
10. route->ifname, net_addr, gate_addr,
11. &flags,&route->refcnt,&route->use,
12. &route->metric, mask_addr,
13. &route->mtu,&route->window,&route->irtt);
15. if((num <10)||!(flags & RTF_UP)){// 必须是活动路由
16. --routelist->number;
17. continue;
18. }
20. route->flags = flags;
22. // 转换十六进制地址
23. sigar_net_address_set(route->destination, hex2int(net_addr, HEX_ENT_LEN));
24. sigar_net_address_set(route->gateway, hex2int(gate_addr, HEX_ENT_LEN));
25. sigar_net_address_set(route->mask, hex2int(mask_addr, HEX_ENT_LEN));
26. }
28. return SIGAR_OK;
29. }
IPv6 配置 (linux_sigar.c:2349-2389)
1. int 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 eth0
7. // addr idx prefix scope flags ifname
9. while(fscanf(fp,"%32s %02x %02x %02x %02x %8s\n",
10. addr,&idx,&prefix,&scope,&flags, ifname)!= EOF)
11. {
12. if(strEQ(name, ifname)){
13. status = SIGAR_OK;
14. break;
15. }
16. }
18. if(status == SIGAR_OK){
19. // 转换 32 字符十六进制地址为 16 字节
20. unsignedchar*addr6 =(unsignedchar*)&(ifconfig->address6.addr.in6);
21. char*ptr = addr;
22. for(i=0; i<16; i++, ptr+=2){
23. addr6[i]=(unsignedchar)hex2int(ptr,2);
24. }
25. ifconfig->prefix6_length = prefix;
26. ifconfig->scope6 = scope;
27. }
29. return status;
30. }
ARP 表 (linux_sigar.c:2659-2742)
1. int sigar_arp_list_get(sigar_t*sigar,sigar_arp_list_t*arplist)
2. {
3. // 读取 /proc/net/arp
4. // 格式:
5. // IP address HW type Flags HW address Mask Device
6. // 192.168.1.1 0x1 0x2 00:11:22:33:44:55 * eth0
8. while(fgets(buffer,sizeof(buffer), fp))){
9. num = sscanf(buffer,"%128s 0x%x 0x%x %128s %128s %16s",
10. net_addr,&type,&flags,
11. hwaddr, mask_addr, arp->ifname);
13. if(num <6){
14. --arplist->number;
15. continue;
16. }
18. arp->flags = flags;
20. // 解析 IP 地址 (支持 IPv4 和 IPv6)
21. status = inet_pton(AF_INET, net_addr,&arp->address.addr);
22. if(status >0){
23. arp->address.family = SIGAR_AF_INET;
24. }
25. elseif((status = inet_pton(AF_INET6, net_addr,&arp->address.addr))>0){
26. arp->address.family = SIGAR_AF_INET6;
27. }
29. // 解析 MAC 地址
30. num = 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 <6){
35. --arplist->number;
36. continue;
37. }
38. arp->hwaddr.family = SIGAR_AF_LINK;
40. // 硬件类型
41. SIGAR_SSTRCPY(arp->type, get_hw_type(type));
42. }
44. return SIGAR_OK;
45. }
TCP 统计 (linux_sigar.c:2420-2462)
1. int 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), fp)){
9. if(strnEQ(buffer, SNMP_TCP_PREFIX,sizeof(SNMP_TCP_PREFIX)-1)){
10. if(fgets(buffer,sizeof(buffer), fp)){
11. status = SIGAR_OK;
12. break;
13. }
14. }
15. }
17. if(status == SIGAR_OK){
18. ptr = sigar_skip_multiple_token(ptr,5);// 跳过前 5 个字段
19. tcp->active_opens = sigar_strtoull(ptr);
20. tcp->passive_opens = sigar_strtoull(ptr);
21. tcp->attempt_fails = sigar_strtoull(ptr);
22. tcp->estab_resets = sigar_strtoull(ptr);
23. tcp->curr_estab = sigar_strtoull(ptr);
24. tcp->in_segs = sigar_strtoull(ptr);
25. tcp->out_segs = sigar_strtoull(ptr);
26. tcp->retrans_segs = sigar_strtoull(ptr);
27. tcp->in_errs = sigar_strtoull(ptr);
28. tcp->out_rsts = sigar_strtoull(ptr);
29. }
31. return status;
32. }
端口到进程映射 (linux_sigar.c:2744-2852)
1. int sigar_proc_port_get(sigar_t*sigar,int protocol,
2. unsignedlong port,sigar_pid_t*pid)
3. {
4. // 1. 获取连接信息 (包含 inode)
5. status = sigar_net_connection_get(sigar,&netconn, port,
6. SIGAR_NETCONN_SERVER|protocol);
8. if(status != SIGAR_OK){
9. return status;
10. }
12. // 2. 遍历 /proc/<pid>/fd,查找匹配的 inode
13. if(!(dirp = opendir(gPROCP_FS_ROOT))){
14. return errno;
15. }
17. while((ent = readdir(dirp))!= NULL){
18. if(!sigar_isdigit(*ent->d_name))continue;
20. // 构造路径: /proc/<pid>/fd
21. memcpy(&fd_name[0], fd_name, len);
22. memcpy(&fd_name[len],"/fd",3);
23. fd_name[len+=3]='\0';
25. if(!(fd_dirp = opendir(fd_name)))continue;
27. while((fd_ent = readdir(fd_dirp))!= 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 == netconn.inode){
35. closedir(fd_dirp);
36. closedir(dirp);
37. *pid = strtoul(ent->d_name, NULL,10);
38. return SIGAR_OK;
39. }
40. }
41. closedir(fd_dirp);
42. }
44. closedir(dirp);
45. return SIGAR_OK;
46. }
NFS 统计 (linux_sigar.c:2464-2599)
1. int 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 = sigar_proc_nfs_gets(file,"proc2", buffer,sizeof(buffer));
9. ptr = sigar_skip_multiple_token(ptr,2);
10. nfs->null = sigar_strtoull(ptr);
11. nfs->getattr = sigar_strtoull(ptr);
12. nfs->setattr = sigar_strtoull(ptr);
13. // ... 其他操作
15. return SIGAR_OK;
16. }
5. 文件系统监控
文件系统列表 (linux_sigar.c:1227-1257)
1. int 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 = setmntent(gMOUNTED,"r"))){
9. return errno;
10. }
12. while(getmntent_r(fp,&ent, buf,sizeof(buf))){
13. fsp =&fslist->data[fslist->number++];
15. fsp->type = SIGAR_FSTYPE_UNKNOWN;
16. SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_dir);// 挂载点
17. SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_fsname);// 设备名
18. SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_type);// 文件系统类型
19. SIGAR_SSTRCPY(fsp->options, ent.mnt_opts);// 挂载选项
21. sigar_fs_type_get(fsp);// 根据 sys_type_name 设置 type
22. }
24. endmntent(fp);
25. return SIGAR_OK;
26. }
文件系统类型 (linux_sigar.c:1172-1225)
1. int sigar_os_fs_type_get(sigar_file_system_t*fsp)
2. {
3. char*type = fsp->sys_type_name;
5. switch(*type){
6. case'e':
7. if(strnEQ(type,"ext",3)){
8. fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
9. }
10. break;
11. case'x':
12. if(strEQ(type,"xfs")|| strEQ(type,"xiafs")){
13. fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
14. }
15. break;
16. // ... 其他类型
17. }
19. return fsp->type;
20. }
文件系统使用情况 (linux_sigar.c:1613-1632)
1. int sigar_file_system_usage_get(sigar_t*sigar,
2. constchar*dirname,
3. sigar_file_system_usage_t*fsusage)
4. {
5. // 使用 statvfs() 获取使用情况
6. int status = sigar_statvfs(sigar, dirname, fsusage);
8. if(status != SIGAR_OK){
9. return status;
10. }
12. fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage);
14. // 获取磁盘 I/O 统计
15. (void)sigar_disk_usage_get(sigar, dirname,&fsusage->disk);
17. return SIGAR_OK;
18. }
6. 磁盘 I/O 监控
磁盘统计 (linux_sigar.c:1519-1611)
1. int sigar_disk_usage_get(sigar_t*sigar,constchar*name,
2. sigar_disk_usage_t*disk)
3. {
4. // 根据内核版本选择统计方法
5. switch(sigar->iostat){
6. case IOSTAT_SYS:
7. // 2.6 内核: /sys/block/<dev>/<dev><partition>/stat
8. status = get_iostat_sys(sigar, name, disk,&iodev);
9. break;
10. case IOSTAT_DISKSTATS:
11. // 2.6 内核: /proc/diskstats
12. status = get_iostat_proc_dstat(sigar, name, disk,&iodev,&device_usage);
13. break;
14. case IOSTAT_PARTITIONS:
15. // 2.4 内核: /proc/partitions
16. status = get_iostat_procp(sigar, name, disk,&iodev);
17. break;
18. case IOSTAT_NONE:
19. default:
20. status = ENOENT;
21. break;
22. }
24. // 计算派生指标
25. if((status == SIGAR_OK)&& iodev){
26. sigar_uptime_get(sigar,&uptime);
28. // 2.6 内核分区没有时间统计,使用整设备统计
29. if(iodev->is_partition &&(sigar->iostat == IOSTAT_DISKSTATS)){
30. partition_usage = disk;
31. disk =&device_usage;
32. }
34. disk->snaptime = uptime.uptime;
36. // 计算服务时间和队列长度
37. interval = disk->snaptime - iodev->disk.snaptime;
38. ios =(disk->reads - iodev->disk.reads)+
39. (disk->writes - iodev->disk.writes);
41. if(disk->time != SIGAR_FIELD_NOTIMPL){
42. tput =((double)ios)* HZ / interval;
43. util =((double)(disk->time - iodev->disk.time))/ interval * HZ;
44. disk->service_time = tput ? util / tput :0.0;
45. }
47. if(disk->qtime != SIGAR_FIELD_NOTIMPL){
48. util =((double)(disk->qtime - iodev->disk.qtime))/ interval;
49. disk->queue= util /1000.0;
50. }
51. }
53. return status;
54. }
/proc/diskstats 方法 (linux_sigar.c:1322-1439)
1. staticint 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. // major minor reads reads_merged reads_sectors reads_ms writes writes_merged writes_sectors writes_ms ios_in_progress ms_weighted_ios
10. // 8 0 123 456 7890 1234 567 890 12345 6789 10 12345
12. while((ptr = fgets(buffer,sizeof(buffer), fp))){
13. unsignedlong major, minor;
15. major = sigar_strtoul(ptr);
16. minor = sigar_strtoull(ptr);
18. if((major == ST_MAJOR(sb))&&
19. ((minor == ST_MINOR(sb))||(minor ==0)))
20. {
21. ptr = sigar_skip_token(ptr);// name
23. num = 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 ==11){// 完整格式
32. disk->rtime = ruse;
33. disk->wtime = wuse;
34. disk->time = use;
35. disk->qtime = aveq;
36. disk->ios = running;
37. }
38. elseif(num ==4){// 旧格式
39. wio = rsect;
40. rsect = rmerge;
41. wsect = ruse;
42. disk->time = disk->qtime = SIGAR_FIELD_NOTIMPL;
43. }
45. disk->reads = rio;
46. disk->writes = wio;
47. disk->read_bytes = rsect;
48. disk->write_bytes = wsect;
50. // 转换扇区为字节 (512 字节/扇区)
51. disk->read_bytes *=512;
52. disk->write_bytes *=512;
54. break;
55. }
56. }
58. return status;
59. }
7. 系统信息
发行版信息 (linux_sigar.c:3017-3079)
1. staticint get_linux_vendor_info(sigar_sys_info_t*info)
2. {
3. // 支持的发行版:
4. // - Fedora: /etc/fedora-release
5. // - SuSE: /etc/SuSE-release
6. // - Gentoo: /etc/gentoo-release
7. // - Slackware: /etc/slackware-version
8. // - Mandrake: /etc/mandrake-release
9. // - VMware: /proc/vmware/version
10. // - XenSource: /etc/xensource-inventory
11. // - Oracle: /etc/oracle-release
12. // - Red Hat: /etc/redhat-release
13. // - LSB: /etc/lsb-release
14. // - Debian: /etc/debian_version
16. linux_vendor_info_t linux_vendors[]={
17. {"Fedora","/etc/fedora-release", NULL },
18. {"SuSE","/etc/SuSE-release", NULL },
19. {"Gentoo","/etc/gentoo-release", NULL },
20. {"Red Hat","/etc/redhat-release", redhat_vendor_parse },
21. {"lsb","/etc/lsb-release", lsb_vendor_parse },
22. {"Debian","/etc/debian_version", NULL },
23. { NULL }
24. };
26. for(i=0; linux_vendors[i].name; i++){
27. vendor =&linux_vendors[i];
29. if(stat(vendor->file,&sb)<0){
30. continue;
31. }
33. status = sigar_file2str(vendor->file, buffer,sizeof(buffer)-1);
34. break;
35. }
37. SIGAR_SSTRCPY(info->vendor, vendor->name);
39. if(vendor->parse){
40. vendor->parse(data, info);
41. }
42. else{
43. generic_vendor_parse(data, info);
44. }
46. return SIGAR_OK;
47. }
系统启动时间 (linux_sigar.c:146-174)
1. staticint sigar_boot_time_get(sigar_t*sigar)
2. {
3. FILE*fp;
4. char buffer[BUFSIZ],*ptr;
5. int found =0;
7. if(!(fp = fopen(gPROC_STAT,"r"))){
8. return errno;
9. }
11. // 读取 /proc/stat 中的 btime 字段
12. while((ptr = fgets(buffer,sizeof(buffer), fp))){
13. if(strnEQ(ptr,"btime",5)){
14. if((ptr = sigar_skip_token(ptr))){
15. sigar->boot_time = sigar_strtoul(ptr);
16. found =1;
17. }
18. break;
19. }
20. }
22. fclose(fp);
24. if(!found){
25. sigar->boot_time = time(NULL);// 回退到当前时间
26. }
28. return SIGAR_OK;
29. }
系统运行时间 (linux_sigar.c:519-532)
1. int sigar_uptime_get(sigar_t*sigar,sigar_uptime_t*uptime)
2. {
3. // 读取 /proc/uptime
4. // 格式: <uptime_seconds> <idle_seconds>
6. status = sigar_file2str(gPROC_UPTIME, buffer,sizeof(buffer));
8. uptime->uptime = strtod(buffer,&ptr);
10. return SIGAR_OK;
11. }
系统负载 (linux_sigar.c:534-551)
1. int 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 = sigar_file2str(gPROC_LOADAVG, buffer,sizeof(buffer));
8. loadavg->loadavg[0]= strtod(buffer,&ptr);// 1 分钟平均
9. loadavg->loadavg[1]= strtod(ptr,&ptr);// 5 分钟平均
10. loadavg->loadavg[2]= strtod(ptr,&ptr);// 15 分钟平均
11. loadavg->processor_queue = strtod(ptr,&ptr);// 进程队列长度
13. return SIGAR_OK;
14. }
系统 UUID (linux_sigar.c:3081-3118)
1. int sigar_sys_info_get_uuid(sigar_t*sigar,char uuid[SIGAR_SYS_INFO_LEN])
2. {
3. // 尝试从 /etc/machine-id 读取 (systemd 系统)
4. FILE*fp = fopen("/etc/machine-id","r");
5. if(fp){
6. if(fgets(uuid, SIGAR_SYS_INFO_LEN -1, fp)){
7. found =1;
8. }
9. fclose(fp);
10. }
11. else{
12. // 回退到磁盘 UUID
13. DIR*ctx = opendir("/dev/disk/by-uuid");
14. if(ctx){
15. while((data = readdir(ctx))!= NULL){
16. constchar*fs_uuid = data->d_name;
17. if(strlen(data->d_name)==36|| strlen(data->d_name)==22){
18. strncat(uuid, fs_uuid, SIGAR_SYS_INFO_LEN - strlen(fs_uuid)-1);
19. found =1;
20. break;
21. }
22. }
23. closedir(ctx);
24. }
25. }
27. return found ? SIGAR_OK : SIGAR_ENOTIMPL;
28. }
容器检测 (linux_sigar.c:3135-3164)
1. int sigar_os_is_in_container(sigar_t*sigar)
2. {
3. // 检查 /proc/1/cgroup
4. // 非容器: 3:cpu:/
5. // 容器: 3:cpu:/docker/58db180b111f8cbc4c08cb58c162c740c728c398cc54bec4586e38d058a028d6
7. snprintf(buffer,sizeof(buffer),"%s/1/cgroup", PROC_FS_ROOT);
9. if(!(fp = fopen(buffer,"r"))){
10. goto out;
11. }
13. while((ptr = fgets(buffer,sizeof(buffer), fp))){
14. // OpenSUSE hack: 跳过包含 "=" 的行 (如 1:name=systemd:/system)
15. tmp_ptr = strstr(buffer,"=");
16. if(tmp_ptr){
17. continue;
18. }
20. tmp_ptr = strstr(buffer, DOCKER_KEY_STR);// ":/docker/"
21. if(tmp_ptr){
22. in_container =1;
23. break;
24. }
25. }
27. fclose(fp);
29. out:
30. return in_container;
31. }
性能优化
1. 进程信息缓存 (linux_sigar.c:718-726)
1. time_t timenow = time(NULL);
3. if(pstat->pid == pid){
4. if((timenow - pstat->mtime)< SIGAR_LAST_PROC_EXPIRE){
5. return SIGAR_OK;// 60 秒内复用缓存
6. }
7. }
9. pstat->pid = pid;
10. pstat->mtime = timenow;
2. 动态缓冲区管理
使用固定大小的栈缓冲区 ( BUFSIZ) 读取 proc 文件,避免频繁内存分配。
3. 字符串解析优化
1. // 跳过标记的宏
2. #define SIGAR_SKIP_SPACE(ptr)while(isspace(*ptr))++ptr
3. #define sigar_skip_token(ptr)(SIGAR_SKIP_SPACE(ptr),*ptr++)
5. // 跳过多个标记
6. ptr = 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 pageshift(x)((x)<< sigar->pagesize)
将页数转换为字节数,避免乘法运算。
2. 十六进制转换 (linux_sigar.c:1824-1844)
1. static SIGAR_INLINE unsignedint hex2int(constchar*x,int len)
2. {
3. int i;
4. unsignedint j;
6. for(i=0, j=0; i<len; i++){
7. registerint ch = x[i];
8. j <<=4;
9. if(isdigit(ch)){
10. j |= ch -'0';
11. }
12. elseif(isupper(ch)){
13. j |= ch -('A'-10);
14. }
15. else{
16. j |= ch -('a'-10);
17. }
18. }
20. return 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, pid,&procargs)== SIGAR_OK &&
4. procargs.number >=1){
5. procname = procargs.data[0];// 从命令行获取完整名称
6. }
7. }
4. CPU 核心合并 (linux_sigar.c:492-500)
1. if(core_rollup &&(i % sigar->lcpu)){
2. // 合并超线程逻辑处理器
3. cpu =&cpulist->data[cpulist->number-1];
4. }
5. else{
6. // 创建新的物理核心条目
7. SIGAR_CPU_LIST_GROW(cpulist);
8. cpu =&cpulist->data[cpulist->number++];
9. }
5. 环境变量解析 (linux_sigar.c:1039-1064)
1. while(ptr < end){
2. char*val = strchr(ptr,'=');
4. if(val == NULL){
5. break;
6. }
8. klen = val - ptr;
9. SIGAR_SSTRCPY(key, ptr);
10. key[klen]='\0';
11. ++val;
13. vlen = strlen(val);
14. status = procenv->env_getter(procenv->data, key, klen, val, vlen);
16. if(status != SIGAR_OK){
17. break;
18. }
20. ptr +=(klen +1+ vlen +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 文件系统,提供了一个统一、高效、跨发行版的系统信息采集接口。其设计体现了以下特点:
- 简洁性: 通过文件系统接口获取信息,代码简洁易懂
- 高效性: 使用缓存和优化解析减少开销
- 兼容性: 支持从 2.2 到 3.x 的各种内核版本
- 可扩展性: 模块化设计,易于添加新功能
- 健壮性: 多级回退机制确保在各种环境都能工作
这种实现方式充分展示了 Linux 内核设计的优势,也为其他平台的系统监控提供了参考。
- 公众号:安全狗的自我修养
- vx:2207344074
- http://gitee.com/haidragon
- http://github.com/haidragon
- bilibili:haidragonx
#
#
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:安全狗的自我修养 haidragon haidragon《跨平台系统信息获取库libsigar源码分析系列(一)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论