What is the Linux CPU Load

The load average represents the average system load over a period of time. It conventionally appears in the form of three numbers which represent the system load during the last one-, five-, and fifteen-minute periods. https://en.wikipedia.org/wiki/Load_(computing)

按照网上看到的一种很形象的说法,假设我们的系统是单 CPU 单内核的,把它比喻成是一条单向马路,把任务比作汽车。

  • 当车不多的时候,load <1;
  • 当车占满整个马路的时候, load=1;
  • 当马路都站满了,而且马路外还堆满了汽车的时候,load>1

Linux 机器上,有很多工具可以查看过去一段时间的平均负载,比如:htoptopwuptime等等。至于它们的数据来源,都是从 /proc/loadavg 读取到的,比如:

$ cat /proc/loadavg
1.07 1.27 0.94 1/3934 1168925

loadavg 的算法及实现

loadavg 的实现在 kernel/sched/loadavg.c 文件里,文件开头写了一段算法说明,摘录如下:

The global load average is an exponentially decaying average of nr_running + nr_uninterruptible.

Once every LOAD_FREQ:

  nr_active = 0;
  for_each_possible_cpu(cpu)
      nr_active += cpu_of(cpu)->nr_running + cpu_of(cpu)->nr_uninterruptible;

  avenrun[n] = avenrun[0] * exp_n + nr_active * (1 - exp_n)

所以 loadavg 是 nr_running + nr_uninterruptible 状态线程数量的一个指数衰减平均,每隔 LOAD_FREQ(5 秒)把过去 n(取 1、5、15) 分钟里的 loadavg 计算一次。

其中的 exp_n = e^(-LOAD_FREQ/(60*n)),比如 exp_1 = 0.92004exp_5 = 0.98347exp_15 = 0.99446

include/linux/sched/loadavg.h 里,定义了几个常数,其目的是使用整数的乘法和移位操作,来避免复杂的浮点和指数运算:

#define FSHIFT          11              /* nr of bits of precision */
#define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
#define LOAD_FREQ       (5*HZ+1)        /* 5 sec intervals */
#define EXP_1           1884            /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5           2014            /* 1/exp(5sec/5min) */
#define EXP_15          2037            /* 1/exp(5sec/15min) */

/*
 * a1 = a0 * e + a * (1 - e)
 */
static inline unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{
        unsigned long newload;

        newload = load * exp + active * (FIXED_1 - exp);
        if (active >= load)
                newload += FIXED_1-1;

        return newload / FIXED_1;
}

比如,当 n = 1 分钟时,这时的 #define EXP_1 1884,所以 load * exp_1 = load * 0.9200 = load * 1884 / 2048,这样就简化为先计算 load * EXP_1,再右移 11 位可以得到结果。

参考资料:

Read More: