top是linux下查看CPU使用率最常见的命令,功能很强大,使用也很方便。但有一点很不好,就是top的工作状态是全屏的,而且不能返回。如果想在C程序里调用top命令获取CPU利用率的话就很困难了。
所以我们需要一简化版的top。
top命令的工作原理是读取/proc文件夹下的stat文件来获取CPU使用率。
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源代码,修改如下:
#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的使用率,用它来度量当前瞬时的使用率还是合理的。