使用Proc文件计算CPU使用率百分比

时间:2013-04-15 09:21:36

标签: c++ c linux

我想在我的程序中专门用一个线程来收集有关其性能的指标。内存使用情况,CPU等。我一直在尝试使用/ proc / stat和/ proc / pid / stat文件。我目前正试图测量%CPU使用率。我的程序报告的值完全不符合“顶级”报告的内容。我在几个不同的Linux发行版上试过这个,并且每个都看到相同的结果。

这是我用来计算百分比的代码。任何人都可以在这里发现任何问题吗?

https://github.com/mmcilroy/cpu_usage

#include <stdlib.h>
#include <sys/types.h>
#include <sys/times.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct pstat {
    long unsigned int utime_ticks;
    long int cutime_ticks;
    long unsigned int stime_ticks;
    long int cstime_ticks;
    long unsigned int vsize; // virtual memory size in bytes
    long unsigned int rss; //Resident  Set  Size in bytes
    long unsigned int cpu_total_time;
};

int get_usage(const pid_t pid, struct pstat* result) {

    //convert  pid to string
    char pid_s[20];
    snprintf(pid_s, sizeof(pid_s), "%d", pid);

    char stat_filepath[30] = "/proc/"; strncat(stat_filepath, pid_s,
            sizeof(stat_filepath) - strlen(stat_filepath) -1);
    strncat(stat_filepath, "/stat", sizeof(stat_filepath) -
            strlen(stat_filepath) -1);

    FILE *fpstat = fopen(stat_filepath, "r");
    if (fpstat == NULL) {
        perror("FOPEN ERROR ");
        return -1;
    }

    FILE *fstat = fopen("/proc/stat", "r");
    if (fstat == NULL) {
        perror("FOPEN ERROR ");
        fclose(fstat);
        return -1;
    }

    //read values from /proc/pid/stat
    bzero(result, sizeof(struct pstat));
    long int rss;
    if (fscanf(fpstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
                "%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
                &result->utime_ticks, &result->stime_ticks,
                &result->cutime_ticks, &result->cstime_ticks, &result->vsize,
                &rss) == EOF) {
        fclose(fpstat);
        return -1;
    }
    fclose(fpstat);
    result->rss = rss * getpagesize();

    //read+calc cpu total time from /proc/stat
    long unsigned int cpu_time[10];
    bzero(cpu_time, sizeof(cpu_time));
    if (fscanf(fstat, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
                &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3],
                &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7],
                &cpu_time[8], &cpu_time[9]) == EOF) {
        fclose(fstat);
        return -1;
    }

    fclose(fstat);

    for(int i=0; i < 4;i++)
        result->cpu_total_time += cpu_time[i];

    printf( "usage: cpu %lu, utime %lu, stime %lu\n", result->cpu_total_time, result->utime_ticks, result->stime_ticks );

    return 0;
}

void calc_cpu_usage_pct(const struct pstat* cur_usage,
                        const struct pstat* last_usage,
                        double* usage)
{
    printf( "delta: cpu %lu, utime %lu, stime %lu\n",
        cur_usage->cpu_total_time - last_usage->cpu_total_time,
        cur_usage->utime_ticks - last_usage->utime_ticks,
        cur_usage->stime_ticks - last_usage->stime_ticks );

    const long unsigned int cpu_diff = cur_usage->cpu_total_time - last_usage->cpu_total_time;
    const long unsigned int pid_diff =
        ( cur_usage->utime_ticks + cur_usage->utime_ticks + cur_usage->stime_ticks - cur_usage->stime_ticks ) -
        ( last_usage->utime_ticks + last_usage->utime_ticks + last_usage->stime_ticks - last_usage->stime_ticks );

    *usage = 100.0 * ( (double)pid_diff / (double)cpu_diff );
}

int main( int argc, char* argv[] )
{
    pstat prev, curr;
    double pct;

    struct tms t;
    times( &t );

    if( argc <= 1 ) {
        printf( "please supply a pid\n" ); return 1;
    }

    while( 1 )
    {
        if( get_usage(atoi(argv[1]), &prev) == -1 ) {
            printf( "error\n" );
        }

        sleep( 5 );

        if( get_usage(atoi(argv[1]), &curr) == -1 ) {
            printf( "error\n" );
        }

        calc_cpu_usage_pct(&curr, &prev, &pct);

        printf("%%cpu: %.02f\n", pct);
    }
}

如果您想亲自尝试一下,该程序需要1个参数 - 监视进程的pid

5 个答案:

答案 0 :(得分:3)

我知道这有点旧,但我可以解释为什么你的新方程有效:(1/INTERVAL) * (pid diff)

这只是基本百分比等式100 * (pid diff) / (cpu diff)的简化,看起来就像你在第一个例子中尝试做的那样。

/ proc / stat中的cpu时间(以及/ proc / pid / stat中的utime和stime)在USER_HZ(或jiffies)中报告。该值通常 1/100秒。这意味着CPU每秒会有100个“抽头”,这意味着你的“CPU差异”将是INTERVAL*100

代替那个,你得到:

  

100 *(pid diff)/(INTERVAL * 100)

取消100's并留下:

  

(pid diff)/ INTERVAL

这与您现在使用的相同。这也意味着如果您确实纠正了顶部代码中存在的问题,那么也应该。 pid diff应为(curr utime + curr stime) - (prev utime + prev stime)。如果它不起作用,那么也许你加载CPU时间的方式是错误的?它很容易测试,因为你知道它应该是什么价值(INTERVAL*100)

由于您现在有一个工作方程,您可能不在乎原始代码的问题,但请记住,如果您尝试在USER_HZ 的系统上使用它1/100,等式无效。

答案 1 :(得分:2)

我检查了top的来源(来自procps)。似乎它基本上执行以下计算...

(1 /间隔)*(utime + stime)

其中间隔是样本之间的秒数。 utime / stime直接从/ proc / pid / stat

中读取

我必须承认我不明白为什么会这样(它不应该根据“man proc”),但是我已经用很多不同的场景对它进行了测试,我的程序的输出总是与“top”的输出相匹配

有兴趣听听有关其工作原理的一些反馈:)

这是我的最新消息来源

#include <stdlib.h>
#include <sys/types.h>
#include <sys/times.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define INTERVAL 3

struct pstat {
    long unsigned int utime_ticks;
    long int cutime_ticks;
    long unsigned int stime_ticks;
    long int cstime_ticks;
    long unsigned int vsize; // virtual memory size in bytes
    long unsigned int rss; //Resident  Set  Size in bytes
};

int get_usage(const pid_t pid, struct pstat* result) {

    //convert  pid to string
    char pid_s[20];
    snprintf(pid_s, sizeof(pid_s), "%d", pid);

    char stat_filepath[30] = "/proc/"; strncat(stat_filepath, pid_s,
            sizeof(stat_filepath) - strlen(stat_filepath) -1);
    strncat(stat_filepath, "/stat", sizeof(stat_filepath) -
            strlen(stat_filepath) -1);

    FILE *fpstat = fopen(stat_filepath, "r");
    if (fpstat == NULL) {
        perror("FOPEN ERROR ");
        return -1;
    }

    //read values from /proc/pid/stat
    bzero(result, sizeof(struct pstat));
    long int rss;
    if (fscanf(fpstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
                "%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
                &result->utime_ticks, &result->stime_ticks,
                &result->cutime_ticks, &result->cstime_ticks, &result->vsize,
                &rss) == EOF) {
        fclose(fpstat);
        return -1;
    }
    fclose(fpstat);
    result->rss = rss * getpagesize();

    return 0;
}

void calc_cpu_usage_pct(const struct pstat* cur_usage,
                        const struct pstat* last_usage,
                        double* usage)
{
    const long unsigned int pid_diff =
        ( cur_usage->utime_ticks + cur_usage->stime_ticks ) -
        ( last_usage->utime_ticks + last_usage->stime_ticks );

    printf( "delta %lu\n", pid_diff );

    *usage = 1/(float)INTERVAL * pid_diff;
}

int main( int argc, char* argv[] )
{
    pstat prev, curr;
    double pct;

    struct tms t;
    times( &t );

    if( argc <= 1 ) {
        printf( "please supply a pid\n" ); return 1;
    }

    while( 1 )
    {
        if( get_usage(atoi(argv[1]), &prev) == -1 ) {
            printf( "error\n" );
        }

        sleep( INTERVAL );

        if( get_usage(atoi(argv[1]), &curr) == -1 ) {
            printf( "error\n" );
        }

        calc_cpu_usage_pct(&curr, &prev, &pct);

        printf("%%cpu: %.02f\n", pct);
    }
}

答案 2 :(得分:0)

linux中的这个命令可能对linux有用。

# apt-get install sysstat
# up2date sysstat
# mpstat 

现在,您将了解如何找到如何将命令行输出作为字符串和解析。您还可以使用mpstat的不同参数。 另请尝试$ top

link获取帮助。

答案 3 :(得分:0)

主循环有点关闭:不是得到“prev”,然后睡觉,然后“下一步”并计算差异,你应该在循环外得到“prev”,并在循环内得到“curr”,计算,将“curr”复制到“prev”然后再循环。这修复了不计算50%使用时间的部分。

答案 4 :(得分:0)

尝试查看top命令源代码,源代码将在busybox中可用

修改: 将mpstat替换为top,因为mpstat显示整体使用情况