从用户的角度来看,优先级非常简单,不过是从-20 ~ 19之间的数字。但是内核内部对这些优先级的处理相当的复杂。
优先级内核表示
在用户空间可以通过系统调用nice更改当前进程的优先级,对于linux系统来说优先级的表示范围是-20~19,值越小表示进程的优先级越高。至于说为什么选择这么一个诡异的范围,真相已经淹没在历史中了。
在内核中使用一个看起来正常得多的表示方法,从0到139。值越小,优先级越低。其中0~99是给实时进程使用的,nice值的-20~19正好可以映射到100到139这个范围内,这也意味着nice只能设置非实时进程的优先级。实时进程的优先级一定比普通进程的优先级高。
计算优先级
上面说的都是进程的静态优先级,有时我们还需要考虑下面三种优先级:
进程的动态优先级task_struct->prio, 普通优先级task_struct->normal_prio,和静态优先级task_struct->static_prio。
动态优先级和普通优先级是通过静态优先级计算出来的:
p->prio = effective_prio(p);
辅助函数effective_prio执行了下列操作:
/*
* Calculate the current priority, i.e. the priority
* taken into account by the scheduler. This value might
* be boosted by RT tasks, or might be boosted by
* interactivity modifiers. Will be RT if the task got
* RT-boosted. If not then it returns p->normal_prio.
*/
static int effective_prio(struct task_struct *p)
{
p->normal_prio = normal_prio(p);
/*
* If we are RT tasks or we were boosted to RT priority,
* keep the priority unchanged. Otherwise, update priority
* to the normal priority:
*/
if (!rt_prio(p->prio))
return p->normal_prio;
return p->prio;
}
这个函数返回动态优先级,同时在函数内部会修改进程的普通优先级。
对于普通进程,我们可以看出static_prio,normal_prio和prio是完全相同的。实时进程的优先级计算方法是不同的,要根据rt_priority计算,由于更高的priority表示更高的优先级,内核内部的优先级表示则完全相反,越低的值表示的优先级越高。
normal_prio非常简单,仅仅返回了p->static_prio,之所以使用一个函数封装这个简单的功能,是因为历史的原因,在原来的O(1)调度器时,普通优先级的计算涉及到很多复杂的技巧性工作。
计算负载权重
优先级固然重要,但是在进程调度时考虑到task-struct->se.load中保存的负荷权重。
函数set_load_weight负责根据进程类型及其静态优先级计算负荷权重。
对于普通进程,内核维护了一个表,从-20~19每个优先级对应表中一项
static const int prio_to_weight[40] = {
/* -20 */ 88761, 71755, 56483, 46273, 36291,
/* -15 */ 29154, 23254, 18705, 14949, 11916,
/* -10 */ 9548, 7620, 6100, 4904, 3906,
/* -5 */ 3121, 2501, 1991, 1586, 1277,
/* 0 */ 1024, 820, 655, 526, 423,
/* 5 */ 335, 272, 215, 172, 137,
/* 10 */ 110, 87, 70, 56, 45,
/* 15 */ 36, 29, 23, 18, 15,
};
可以看出数组中各个相邻值之间的乘数因子是1.25, 共有40项,prio_to_weight[0]对应着优先级100,prio_to_wright[39]对应着优先级139。而是是进程的负载则是prio_to_weight[0]的2倍。其实我们完全没必要记住这些奇怪的数字,只要知道实时进程权重更大即可