读取proc信息的可扩展实现
需求
1. 将内存、线程数等信息注册到zk上进行监控
2. 统计信息,为下一步做负载均衡做准备。
实现
本文只解决问题1。
从网上查询了下,这些信息可以从proc文件系统中获取,如果不知道proc的,可以Google下。
网上有读取proc信息的lib——libproc,即 procps , 据说htop等实现就是基于它的。
我下载下来了,include和lib都生成了,好不容易找到一篇教程,结果在
stackoverflow上,见有人说有内存泄露,需要如下方法做。
int main(int argc, char** argv) { // fillarg used for cmdline // fillstat used for cmd PROCTAB* proc = openproc(PROC_FILLARG | PROC_FILLSTAT); while (proc_t* proc_info = readproc(proc, NULL)) { // do something freeproc(proc_info) } closeproc(proc); }
于是看proc_t的定义,充满了上世纪的风格,似乎迷失在信息各种信息中了,于是无奈放弃了,直接手撸吧。代码稍后附上,如下是几个关键的技术点
1. 即然shell 命名可以获取proc信息,在C中,我们可以通过popen建立管道,获取shell命令的输出。
2. 获取进程号,可以通过 getpid()
3. sizeof(xx)/sizeof(xx[0]) 可以获取数组的大小。
4. 设计了ZeroHelper 结构,方便了扩展
5. offsetof 宏用于获取元素的偏移
6. 函数指针的应用恰到好处。
#include <time.h> #include <stddef.h> #include <string.h> #include <stdint.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include<unistd.h> struct StatInfo { time_t start_time; uint32_t duration_sec; uint32_t conn_cnt; uint32_t pmem_mb; // peak virtual memory uint32_t vmem_mb; // virtual memory uint32_t rmem_mb; // real memory uint32_t thread_cnt;// 线程数 uint32_t cpu_usage; // cpu }; typedef int (*convert_fn)(const char* str, void* value_ptr); struct ZeroHelper { const char* key; uint32_t key_len; uint32_t value_offset; convert_fn fn; }; int proc_stat_mem_convert(const char* str, void* value_ptr) { uint32_t* value = (uint32_t*)value_ptr; while (*str && isspace(*str)) ++str; *value = atoi(str); return 0; } static const ZeroHelper proc_mem_convert_array[] = { {"VmPeak:", sizeof("VmPeak:") - 1, offsetof(StatInfo, pmem_mb), proc_stat_mem_convert}, {"VmSize:", sizeof("VmSize:") - 1, offsetof(StatInfo, vmem_mb), proc_stat_mem_convert}, {"VmRSS:", sizeof("VmRSS:") - 1, offsetof(StatInfo, rmem_mb), proc_stat_mem_convert}, {"Threads:", sizeof("Threads:") - 1, offsetof(StatInfo, thread_cnt), proc_stat_mem_convert}, }; int fresh_memstat_info(pid_t pid, StatInfo* info) { char proc_cmd [1024]; snprintf(proc_cmd, sizeof(proc_cmd), "cat /proc/%d/status", pid); FILE* fp = popen(proc_cmd, "r"); char proc_line[1024]; const ZeroHelper* helper = &(proc_mem_convert_array[0]); const int kHelperLen = sizeof(proc_mem_convert_array)/sizeof(proc_mem_convert_array[0]); int j = 0; while (fgets(proc_line, sizeof(proc_line), fp) != NULL) { if (j >= kHelperLen) { break; } // 忽略 key 头部 if (strncmp(proc_line, helper->key, helper->key_len) == 0) { helper->fn(proc_line + helper->key_len, (char*)info + helper->value_offset); ++helper; ++j; } } pclose(fp); return 0; } int main() { StatInfo info; pid_t pid = getpid(); fresh_memstat_info(pid, &info); printf("%d\t%ukB\t%ukB\t%ukB\t%u\n", pid, info.pmem_mb, info.vmem_mb, info.rmem_mb, info.thread_cnt); sleep(1000); return 0; }