【问题标题】:Calculating %CPU Usage Using Proc Files使用 Proc 文件计算 %CPU 使用率
【发布时间】:2013-04-15 09:21:35
【问题描述】:

我想在我的程序中专门设置一个线程来收集有关其性能的指标。内存使用情况、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

【问题讨论】:

  • 因此,请发布您的直接进行计算的代码,而不是作为链接。肯定不会超过 30-40 行吧?
  • 将 cur_usage->utime_ticks 表示为“a”,cur_usage->stime_ticks 表示为“b”,last_usage->utime_ticks 表示为“c”,last_usage->stime_ticks 表示为“d”。然后计算 pid_diff 为“(a+a+b-b) - (c+c+d-d)”,即 2a-2c。不确定是否应该考虑“b”和“d”,但这里的因素2不是可疑的吗?
  • 是的,你是对的。这是代码中的错误。即使在我修复它之后仍然无法正常工作:)
  • 你知道getrusage(2)times(2)吗?更便携更容易访问。

标签: c++ c linux


【解决方案1】:

我知道这有点老了,但我可以解释为什么你的新公式有效:(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 diff”将为INTERVAL*100

代入,你得到:

100 * (pid 差异) / (INTERVAL * 100)

取消 100,你就剩下:

(pid 差异)/间隔

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

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

【讨论】:

    【解决方案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);
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      # apt-get install sysstat
      # up2date sysstat
      # mpstat 
      

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

      从此link获得帮助。

      【讨论】:

      • 谢谢 - 但我想从我的程序内部执行此操作而不产生新的 shell
      【解决方案4】:

      主循环有点偏离:不是获取“prev”,然后休眠,然后获取“next”并计算差异,您应该在循环外获取“prev”,在循环内部获取“curr”,计算,将“curr”复制到“prev”,然后再次循环。这修复了未计算 50% 使用时间的部分。

      【讨论】:

      • 对不起,我没有关注。这不是和我现在做的基本一样吗?我以 5 秒的间隔对 CPU / 进程使用情况进行采样,并根据这两个样本之间的差异计算百分比
      • 是的。只是风格问题。我现在意识到我应该把它写成评论,而不是答案。我的错。
      【解决方案5】:

      试试看上面的命令源码,busybox里会有源码

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

      【讨论】:

      • 除非我弄错了 mpstat 只测量每个 cpu 的使用率,而不是每个进程的 % cpu 使用率。为此,它只需要查看 /proc/stat
      • 抱歉,我已经编辑了评论,谢谢。请看一下busybox代码查找每个进程的cpu使用情况(grep for the "/proc/stat" in busybox目录)
      猜你喜欢
      • 1970-01-01
      • 2011-07-27
      • 2012-06-13
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-23
      • 2021-03-13
      • 2010-10-07
      相关资源
      最近更新 更多