2007-10-03
Linux下瞬时cpu使用率的统计
top是linux下查看cpu使用率最常见的命令,功能很强大,使用也很方便,是系统管理的好帮手。
在bash脚本中集成top命令获取cpu使用率很简单,但是如果我们想要在C代码中获取当前cpu占用率,又该如何处理呢?
首先要了解一下top的工作原理:
1、linux系统通过/proc/stat伪文件系统来输出当前的cpu使用情况,而top正是通过读取此文件中的数据来实现cpu使用情况的统计。
2、cpu占用率并不是一个瞬时概念,而是一个短时间内的统计值,因此无法通过瞬时计算得出,而必须通过一定时间差内的统计来实现。
stat的文件结构:
stat文件的前几行为CPU使用信息描述。
具体有多少行取决于系统有多少块CPU,但行中的字段都是相同的。第一行以字符串’CPU’开头,为系统总的CPU使用情况。其余行以’CPU’+CPUID开头,用以分别描述单个CPU。
第一行内容如下:
cpu 15718 197 2574 136003 3881 690 531 0
数字的单位为USER_HZ(1/100ths of a second on most architectures)。
第一个字符串为CPU标识,其后四个数字依次为在用户态消耗的时间片,在低优先级的用户态消耗的时间片,内核态消耗的时间片以及空闲时间片。
2.4内核中,以上即为CPU使用信息的全部内容。但在2.6内核中,这一行还多出了三个额外的数据。分别为 iowait,irq,softirq等操作所耗费的时间片。
了解了以上信息后,我们就可以开始准备编码了~ 不过,最好还是有个参考~
参照busybox的top源代码,我们实现自己的cpu统计程序如下:
#include <stdio.h> #include <string.h> #include <malloc.h> #include <errno.h> #define SMLBUFSIZ 256 typedef unsigned long long ul_t; typedef struct cpu_t { ul_t u, n, s, i, w, x, y, z; // as represented in /proc/stat ul_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display } cpu_t; cpu_t *cpus_refresh (cpu_t *cpus) { static FILE *fp = NULL; int i; int num; char buf[SMLBUFSIZ]; if (!fp) { if (!(fp = fopen("/proc/stat", "r"))) fprintf(stderr,"Failed /proc/stat open: %s", strerror(errno)); } rewind(fp); fflush(fp); // first value the last slot with the cpu summary line if (!fgets(buf, sizeof(buf), fp)) fprintf(stderr,"failed /proc/stat read"); cpus->x = 0; cpus->y = 0; cpus->z = 0; num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &cpus->u, &cpus->n, &cpus->s, &cpus->i, &cpus->w, &cpus->x, &cpus->y, &cpus->z ); if (num < 4) fprintf(stderr,"failed /proc/stat read"); return cpus; } static void format_output (cpu_t *cpu, const char *pfx) { #define TRIMz(x) ((tz = (long long)(x)) < 0 ? 0 : tz) long long u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz; float scale; u_frme = cpu->u - cpu->u_sav; s_frme = cpu->s - cpu->s_sav; n_frme = cpu->n - cpu->n_sav; i_frme = TRIMz(cpu->i - cpu->i_sav); w_frme = cpu->w - cpu->w_sav; x_frme = cpu->x - cpu->x_sav; y_frme = cpu->y - cpu->y_sav; z_frme = cpu->z - cpu->z_sav; tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme; if (tot_frme < 1) tot_frme = 1; scale = 100.0 / (float)tot_frme; fprintf(stderr,"%s: %.2f%%us, %.2f%%sy, %.2f%%ni, %.2f%%id, %.2f%%wa, %.2f%%hi, %.2f%%si, %.2f%%st\n", pfx, (float)u_frme * scale, (float)s_frme * scale, (float)n_frme * scale, (float)i_frme * scale, (float)w_frme * scale, (float)x_frme * scale, (float)y_frme * scale, (float)z_frme * scale); cpu->u_sav = cpu->u; cpu->s_sav = cpu->s; cpu->n_sav = cpu->n; cpu->i_sav = cpu->i; cpu->w_sav = cpu->w; cpu->x_sav = cpu->x; cpu->y_sav = cpu->y; cpu->z_sav = cpu->z; #undef TRIMz } int main(){ cpu_t * cpu; cpu =(cpu_t *)malloc(sizeof(cpu_t)); memset(cpu,0,sizeof(cpu_t)); while(1){ cpus_refresh(cpu); format_output(cpu,"Cpu usage: "); sleep(1); } }
需要注意的是,无论用什么方法,我们都无法获取到系统瞬时的CPU使用率,而只能通过近期的使用情况来推测。
在这个程序中,第一次调用cpus_refresh时返回的并不是当前的CPU使用率,而是从系统启动以来至今为止的CPU使用率,这并不能反映当前的CPU使用情况。第二次调用计算差值后得到的是这一秒内CPU的使用率,用它来度量当前瞬时的使用率还是合理的。
–EOF-