CPU 负载

Linux 通过 /proc/stat/proc/uptime 导出各种信息,用户态工具(例如 top(1))使用这些信息来计算系统在特定状态下花费的平均时间,例如

$ iostat
Linux 2.6.18.3-exp (linmac)     02/20/2007

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          10.01    0.00    2.92    5.44    0.00   81.63

...

这里系统认为在默认的采样周期内,系统有 10.01% 的时间在用户空间执行工作,2.92% 的时间在内核中,总体上有 81.63% 的时间处于空闲状态。

在大多数情况下,/proc/stat 信息相当真实地反映了现实情况,但是,由于内核收集此数据的方式/时间性质,有时它根本不可信。

那么这些信息是如何收集的呢?每当发出定时器中断信号时,内核会查看此时正在运行的任务类型,并增加与此任务类型/状态相对应的计数器。问题在于,在两个定时器中断之间,系统可能已经在各种状态之间切换多次,但计数器仅针对最后一个状态递增。

示例

如果我们想象一个系统,其中一项任务以以下方式周期性地消耗周期

 time line between two timer interrupts
|--------------------------------------|
 ^                                    ^
 |_ something begins working          |
                                      |_ something goes to sleep
                                     (only to be awaken quite soon)

在上述情况下,根据 /proc/stat,系统负载将为 0% (因为定时器中断始终发生在系统执行空闲处理程序时),但实际上负载接近 99%。

人们可以想象更多的情况,其中内核的这种行为将导致 /proc/stat 内的信息非常不稳定

/* gcc -o hog smallhog.c */
#include <time.h>
#include <limits.h>
#include <signal.h>
#include <sys/time.h>
#define HIST 10

static volatile sig_atomic_t stop;

static void sighandler(int signr)
{
        (void) signr;
        stop = 1;
}

static unsigned long hog (unsigned long niters)
{
        stop = 0;
        while (!stop && --niters);
        return niters;
}

int main (void)
{
        int i;
        struct itimerval it = {
                .it_interval = { .tv_sec = 0, .tv_usec = 1 },
                .it_value    = { .tv_sec = 0, .tv_usec = 1 } };
        sigset_t set;
        unsigned long v[HIST];
        double tmp = 0.0;
        unsigned long n;
        signal(SIGALRM, &sighandler);
        setitimer(ITIMER_REAL, &it, NULL);

        hog (ULONG_MAX);
        for (i = 0; i < HIST; ++i) v[i] = ULONG_MAX - hog(ULONG_MAX);
        for (i = 0; i < HIST; ++i) tmp += v[i];
        tmp /= HIST;
        n = tmp - (tmp / 3.0);

        sigemptyset(&set);
        sigaddset(&set, SIGALRM);

        for (;;) {
                hog(n);
                sigwait(&set, &i);
        }
        return 0;
}

参考

鸣谢

Con Kolivas, Pavel Machek