我们在运行top 或者uptime时,会计算当前系统的负载平衡。
当前系统的负载平衡是看我们cpu对于当前执行任务的一个执行情况。同时也判断我们系统的任务
对于cpu的需求情况。
这句话是对于负载平衡的一个很好的总结:

The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.

以下是我收集的负载平和的计算方法
负载的查看

在Linux系统中,使用uptime、w和top命令都可以查看负载,或者直接通过cat /proc/loadavg查看。

比如说uptime命令:

20:56:33 up 22 min, 3 users, load average: 0.39, 0.25, 0.10

可以看到有三个值,这三个值分别表示:

前1分钟系统的平均负载
前5分钟系统的平均负载
前15分钟系统的平均负载

下面就来看看负载的含义。
负载的含义

负载的含义是运行队列的平均长度。

具体而言,对于Linux系统,其统计了最近1分钟、最近5分钟、最近15分钟处于running或者uninterruptible sleep状态的进程数。

文末参考资料里给出了一个车道的例子进行类比,这儿就不赘述了。举几个例子:

负载为0.5,表示CPU有平均一半的时间是空闲的
负载为1,这时就表示CPU被充分利用了
负载为2,则表示负载过大,有一半的任务在等待CPU执行

一般认为负载为0.7是警戒线。
负载与多处理器

对于多处理器,相当于多车道。比如对于双处理器,那么负载为2才算满。 可以简单地将负载除以CPU核心数换算成单处理的情况。
负载与CPU使用率

文末参考资料中提到:

The CPU percentage shows us how much the cars are using the freeway, but the load averages show us the whole picture, including pent-up demand.

相比于CPU使用率,负载可能展示更多的信息,即CPU的需求量。
负载和多线程

Wikipedia提到在不同的Unix系统中多线程负载的计算方式有很多种,比如线程与核实体间不同的模型就有所不同。而在Linux系统中每个线程应该都是独立计算的。
负载的计算

我并不了解统计学等知识,以下只是个人一些粗浅的理解。

负载计算原理是统计学的EWMA(Exponentially Damped/Weighted Moving Average),利用指数衰减函数递推出当前序列平均值。

Wikipedia里提到:

Hence, the 1-minute load average consists of 63% (more precisely: 1 - 1/e) of the load from the last minute and 37% (1/e) of the average load since start up, excluding the last minute. For the 5- and 15-minute load averages, the same 63%/37% ratio is computed over 5 minutes and 15 minutes respectively. Therefore, it's not technically accurate that the 1-minute load average only includes the last 60 seconds of activity (it actually includes 37% of the activity from the past), but it is correct to state that it includes mostly the last minute.

即统计计算出一段时间的负载,其本质是计算均值。比如说:

要计算——最近x分钟负载:$la_{-x}$
    假设已知——自系统启动到x分钟前的负载为:$la_{+x}$
    假设可以得到——近x分钟的运行的任务数为:$n_x$

那么 emmc 负载均衡 负载均衡ucmp_emmc 负载均衡emmc 负载均衡 负载均衡ucmp_多处理器_02

可以看出emmc 负载均衡 负载均衡ucmp_多处理器_03分钟前的负载权重是比较小的,随着时间的推移前面的信息影响会越来越小,直到趋近于0,最近时间的信息影响的比重比较大。

而在Linux系统中,是每间隔emmc 负载均衡 负载均衡ucmp_x系统_04或者emmc 负载均衡 负载均衡ucmp_多处理器_05统计一次运行的任务数,然后计算均值。那么随着时间推移,可以得出这么一个序列:

emmc 负载均衡 负载均衡ucmp_x系统_06

当前时刻emmc 负载均衡 负载均衡ucmp_emmc 负载均衡_07的Moving Average——emmc 负载均衡 负载均衡ucmp_emmc 负载均衡_08同样根据emmc 负载均衡 负载均衡ucmp_多处理器_09计算出:

emmc 负载均衡 负载均衡ucmp_x系统_10

对于要统计最近1分钟(60秒)的均值,由于时间间隔为5秒,那么emmc 负载均衡 负载均衡ucmp_多处理器_11的取值应为emmc 负载均衡 负载均衡ucmp_x系统_12;同理,最近5分钟(300秒)的均值,emmc 负载均衡 负载均衡ucmp_多处理器_11的取值应为emmc 负载均衡 负载均衡ucmp_emmc 负载均衡_14;最近15分钟(900秒)的均值,emmc 负载均衡 负载均衡ucmp_多处理器_11的取值应为emmc 负载均衡 负载均衡ucmp_emmc 负载均衡_16

下面来看看Linux中如何实现负载的计算。
负载计算的实现

Linux内核中维护了一个Jiffies变量,记录自开机一开经历的Tick数,每次更新Jiffies时系统会调用timer.c中的do_timer();

unsigned long avenrun[3];
static inline void (unsigned long ticks)
{
	unsigned long active_tasks; 
	static int count = LOAD_FREQ;
	count -= ticks;
	if (count < 0) {
		count += LOAD_FREQ;
		active_tasks = count_active_tasks();
		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
		CALC_LOAD(avenrun[1], EXP_5, active_tasks);
		CALC_LOAD(avenrun[2], EXP_15, active_tasks);
	}
}

在sched.h中可以看到上面calc_load()里一些变量、宏的定义:

extern unsigned long avenrun[];	/* Load averages */
#define FIXED_1  (1<<FSHIFT)	/* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ)	/* 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) */
#define CALC_LOAD(load,exp,n) 
	load *= exp; 
	load += n*(FIXED_1-exp); 
	load >>= FSHIFT;

可以看到,每次Jiffies+1时调用calc_load(),并不会马上统计计算负载信息,而是等到一个LOAD_FREQ的周期结束,随后又开始新的LOAD_FREQ周期;

通过调用count_active_tasks()可以获得处于running或者uninterruptible sleep状态的进程数;

最后执行三次的CALC_LOAD,分别更新统计的1分钟、5分钟和15分钟的负载信息。

值得一提的就是CALC_LOAD的实现,其本质是将上面的Moving Average计算的式子乘以emmc 负载均衡 负载均衡ucmp_取值_17,最后再除以emmc 负载均衡 负载均衡ucmp_取值_17,采用位运算并且避免了浮点数运算,提高了计算效率。