【IT168技术文档】在每个运行队列struct rq里,load代表当前运行队列的负载,同时还有一个cpu_load[]这样的数组,在我理解,它是一个分级别的代表当前运行队列负载的“替身”。在多cpu调度时,会计算不同的cpu domain的负载,根据不同的index, 会选取相应的cpu_load[]作为当前运行队列的负载返回。
在每个tick处理函数scheduler_tick()中,会调用update_cpu_load(),来更新当前运行队列的负载,便于后面cpu平衡调度时选取最忙的cpu.比如调度函数find_busiest_group() ,它的工作是:find_busiest_group finds and returns the busiest CPU group within the domain. 它会通过两 个函数source_load()和 target_load()来计算并返回当前运行队列的负载。例如 target_load()是这样的:
1 static unsigned long target_load(int cpu, int type)
2 {
3 struct rq *rq = cpu_rq(cpu);
4 unsigned long total = weighted_cpuload(cpu); 返回当前run queue的load
5
6 if (type == 0 || !sched_feat(LB_BIAS))
7 return total;
8
9 return max(rq->cpu_load[type-1], total); 返回相应 "替身" 与 运行队列负载的较大者
10 }
11
2 {
3 struct rq *rq = cpu_rq(cpu);
4 unsigned long total = weighted_cpuload(cpu); 返回当前run queue的load
5
6 if (type == 0 || !sched_feat(LB_BIAS))
7 return total;
8
9 return max(rq->cpu_load[type-1], total); 返回相应 "替身" 与 运行队列负载的较大者
10 }
11
这两 个函数会有个参数type,它的目的就是选取相应的cpu_load[]。
在系统初始化过程中,会把cpu_load默认为0. 有兴趣的朋友可以参考kernel/sched.c sched_init()函数,linux 2.6.28 line:8286,片断代码如下:
for (j = 0; j < CPU_LOAD_IDX_MAX; j++)
rq->cpu_load[j] = 0;
那么后面这个数组是怎么变化的呢?它的变化趋势是什么样子的呢?
我觉得update_cpu_load()还是比较有意思,来一起分析下。
1 /*
2 * Update rq->cpu_load[] statistics. This function is usually called every
3 * scheduler tick (TICK_NSEC).
4 */
5 static void update_cpu_load(struct rq *this_rq) //当前运行队列指针作为函数参数
6 {
7 unsigned long this_load = this_rq->load.weight; //当前运行队列的负载值
8 int i, scale;
9
10 this_rq->nr_load_updates++; //代表load的更新次数 每一个tick都会加1 ,真够忙的。
11
12 /* Update our load: */ //CPU_LOAD_IDX_MAX在运行队列结构体中=5,这儿对5个等级的cpu_load[]进行更新。
13 for (i = 0, scale = 1; i < CPU_LOAD_IDX_MAX; i++, scale += scale) {
14 unsigned long old_load, new_load;
15
16 /* scale is effectively 1 << i now, and >> i divides by scale */ //请注意,这个scale就是2 的i 次幂。它
17 的值 分别是 1 2 4 8 16
18
19 old_load = this_rq->cpu_load; //当前cpu_load[]数组里面的值
20 new_load = this_load; //当前运行队列的负载值
21 /*
22 * Round up the averaging division if load is increasing. This
23 * prevents us from getting stuck on 9 if the load is 10, for
24 * example.
25 */ round up://目的 如果load是在增长的,不要把余数别浪费了
26 if (new_load > old_load)
27 new_load += scale-1;
28 this_rq->cpu_load = (old_load*(scale-1) + new_load) >> i; //这个公式,我会下面分析。它的意思就是根据当前运行队列的负载以及上次cpu_load[]的数值,计算出当前cpu_load[]应该的变化。
29 }
30 }
31
2 * Update rq->cpu_load[] statistics. This function is usually called every
3 * scheduler tick (TICK_NSEC).
4 */
5 static void update_cpu_load(struct rq *this_rq) //当前运行队列指针作为函数参数
6 {
7 unsigned long this_load = this_rq->load.weight; //当前运行队列的负载值
8 int i, scale;
9
10 this_rq->nr_load_updates++; //代表load的更新次数 每一个tick都会加1 ,真够忙的。
11
12 /* Update our load: */ //CPU_LOAD_IDX_MAX在运行队列结构体中=5,这儿对5个等级的cpu_load[]进行更新。
13 for (i = 0, scale = 1; i < CPU_LOAD_IDX_MAX; i++, scale += scale) {
14 unsigned long old_load, new_load;
15
16 /* scale is effectively 1 << i now, and >> i divides by scale */ //请注意,这个scale就是2 的i 次幂。它
17 的值 分别是 1 2 4 8 16
18
19 old_load = this_rq->cpu_load; //当前cpu_load[]数组里面的值
20 new_load = this_load; //当前运行队列的负载值
21 /*
22 * Round up the averaging division if load is increasing. This
23 * prevents us from getting stuck on 9 if the load is 10, for
24 * example.
25 */ round up://目的 如果load是在增长的,不要把余数别浪费了
26 if (new_load > old_load)
27 new_load += scale-1;
28 this_rq->cpu_load = (old_load*(scale-1) + new_load) >> i; //这个公式,我会下面分析。它的意思就是根据当前运行队列的负载以及上次cpu_load[]的数值,计算出当前cpu_load[]应该的变化。
29 }
30 }
31