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的使用率,用它来度量当前瞬时的使用率还是合理的。

Logo

更多推荐