调度器的一般原理是,根据所能分配的计算能力,向系统中的每个进程提供最大的公平性。调度器分配的资源就是CPU的时间,尽量保证每个进程都获得相同的CPU时间。Linux的CFS调度系统不同于O(1)调度器,不需要时间片概念,至少不需要传统的时间片。CFS调度系统只考虑进程的等待时间,即进程在就绪队列中已经等待了多长时间。但是并非系统上的所有进程都同样重要,调度器也要保证一些重要的进程优先执行,或者要获得更多的CPU时间。对于应用层来说,进程的重要性就是通过优先级来标识的,而CFS调度器在计算进程的虚拟运行时间或者调度延迟时都是使用的权重,下面我们来看一下这两者是如何计算和转换的。
1. 进程优先级
普通进程的优先级由task_struct结构中的prio、static_prio和normal_prio三个成员来描述。static_prio是静态优先级,默认值是在进程创建时从父进程继承过来的,可以使用nice()、sched_setscheduler()或者setpriority()修改。normal_prio是普通优先级,是根据进程的静态优先级和调度策略计算出来的优先级。prio是动态优先级,调度器会根据该成员表示的优先级来给进程分配CPU时间。由于在某些情况下会暂时提高进程的优先级,因此需要3个成员来表示进程的优先级,在提高进程优先级运行的持续时间中,普通和静态优先级的值是不变的。这三个成员的值越低,优先级越高。
实时进程的优先级是由task_struct结构中的rt_priority成员来描述的,该成员保存的值不会代替上面的三个成员。该成员的值越大,优先级越高,实时进程的优先级计算方法和普通进程不同,后面我们会看到。
2. 进程优先级的计算
进程创建时,在copy_process()中调用的dup_task_struct()会将父进程的所有内容都拷贝到子进程的task_struct结构实例中,所以初始时父子进程中所有描述优先级的成员的值都是相同的。在初始化完成后,会调用sched_fork()和调度系统交互,将子进程添加到调度系统中。在sched_fork()中会调整子进程的优先级,相关代码如下所示:
void sched_fork(struct task_struct *p, int clone_flags)
{
......
/*
* Revert to default priority/policy on fork if requested.
*/
if (unlikely(p->sched_reset_on_fork)) {
if (p->policy == SCHED_FIFO || p->policy == SCHED_RR) {
p->policy = SCHED_NORMAL;
p->normal_prio = p->static_prio;
}
if (PRIO_TO_NICE(p->static_prio) < 0) {
p->static_prio = NICE_TO_PRIO(0);
p->normal_prio = p-
{
......
/*
* Revert to default priority/policy on fork if requested.
*/
if (unlikely(p->sched_reset_on_fork)) {
if (p->policy == SCHED_FIFO || p->policy == SCHED_RR) {
p->policy = SCHED_NORMAL;
p->normal_prio = p->static_prio;
}
if (PRIO_TO_NICE(p->static_prio) < 0) {
p->static_prio = NICE_TO_PRIO(0);
p->normal_prio = p-