1

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

4

5 回答 5

4

我知道这有点老了,但我可以解释为什么你的新方程有效:(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 的系统上使用它,则方程式将无效。

于 2013-09-03T21:22:13.873 回答
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);
    }
}
于 2013-04-15T21:22:13.703 回答
0

The main loop is somewhat off: instead of getting "prev", then sleeping, then getting "next" and calculating the difference, you should get "prev" once outside the loop, and inside the loop get "curr", calculate, the copy "curr" into "prev" and then loop again. This fixes the part where 50% of the used time is not counted.

于 2013-04-15T10:01:36.570 回答
0

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

# apt-get install sysstat
# up2date sysstat
# mpstat 

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

从此链接获得帮助。

于 2013-04-15T09:54:20.317 回答
0

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

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

于 2013-04-15T10:08:30.130 回答