读取系统 load average
uptime 或者 top 可以显示系统的 load average. 比如:
[root@localhost ~]# uptime
15:54:04 up 106 days, 6:35, 7 users, load average: 1.45, 1.76, 2.02
三个数字分别表示1,5,15分钟的数据。
load average 中的 load
load average 显示的并不是cpu 使用百分比。这也许是最容易产生的误解。这个误解有两方面的问题:
- load average 并非指 cpu load average 而是 system load average。远古时代(1993年之前)的确是指cpu load,也就是统计系统中处于 runnable 的线程。后来uninterruptible 的线程被计入统计,为了同时体现 I/O 或者 lock 的 wait 情况,此种情况系统并没有真正的 idle。此后这个指标的名字就由 cpu load average 改成了 system load average。
- 并非百分比,而是所有非 idle 线程的总数。需要提醒的是该数字并没有对系统核数做归一化,如果系统中有48个核而你看到的 load average 是20,那么系统基本是比较轻松的。也许当数值突破50之后才会感到压力山大,当然 it depends。
load average 中的 average
这个 average 并不是真的 average,而是采用一种指数阻尼的算法。Linux 引入了一些 magic number 简化计算。直接上代码:
// include/linux/sched/loadavg.h
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
/* 每5秒采样一次 */
#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) */
/* n 表示 active task 的数量,也就是非 idle 线程的数量 */
#define CALC_LOAD(load,exp,n) \
load *= exp; \
load += n*(FIXED_1-exp); \
load >>= FSHIFT;
extern void calc_global_load(unsigned long ticks);
// kernel/sched/loadavg.c
/*
* calc_load - update the avenrun load estimates 10 ticks after the
* CPUs have updated calc_load_tasks.
*
* Called from the global timer code.
*/
void calc_global_load(unsigned long ticks)
{
unsigned long sample_window;
long active, delta;
sample_window = READ_ONCE(calc_load_update);
if (time_before(jiffies, sample_window + 10))
return;
/*
* Fold the 'old' NO_HZ-delta to include all NO_HZ cpus.
*/
delta = calc_load_nohz_fold();
if (delta)
atomic_long_add(delta, &calc_load_tasks);
active = atomic_long_read(&calc_load_tasks);
active = active > 0 ? active * FIXED_1 : 0;
avenrun[0] = calc_load(avenrun[0], EXP_1, active);
avenrun[1] = calc_load(avenrun[1], EXP_5, active);
avenrun[2] = calc_load(avenrun[2], EXP_15, active);
WRITE_ONCE(calc_load_update, sample_window + LOAD_FREQ);
/*
* In case we went to NO_HZ for multiple LOAD_FREQ intervals
* catch up in bulk.
*/
calc_global_nohz();
}
直观的讲,如果之前系统一直处于 idle 状态,在单核系统下增加一个100% load 的线程,一分钟之后的 load average (1min) 为 0.62 而不是线性 average 的结果 1。
参考
http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html