Linux任务调度的时机
linux进程的调度时机大致分为两种情况: 一种是进程自愿调度;另一种是发生强制性调度。 首先,自愿的调度随时都可以进行。在内核空间中,进程可以通过schedule()启动一次调度;在用户空间中,可以通过系统调用pause()达到同样的目的。如果要为自愿的暂停行为加上时间限制,在内核中使用schedule_time(),而在用户空间则使用nanosleep()系统调用。
linux中,强制性的调度发生在每次从系统调用返回的前夕,以及每次中断或异常处理返回用户空间的前夕。应注意的是,从内核态返回到用户态是进程调度发生的必要条件,而不是充分条件,还要取决于当进程task_struct结构中的need_resched是否为1。
从进程调度的时机可以看出,内核的调度方式为“有条件的剥夺方式”。当进程在用户空间运行,不管自愿不自愿,一旦有必要比如时间片用完,内核就可以暂时剥夺其运行而调度其他进程运行。而进程一旦进入内核空间,即进入核心态时,尽管知道应该要调度了,但实际上却不会发生,一直要到该进程返回到用户空间前夕才能剥夺其运行。
Linux任务调度策略
Linux支持SCHED_FIFO、SCHED_RR和SCHED_OTHER的调度策略。
linux用函数goodness()统一计算进程(包括普通进程和实时进程)的优先级权值,该权值衡量一个处于可运行状态的进程值得运行的程度,权值越大,进程优先级越高。 每个进程的task_struct结构中,与goodness()计算权值相关的域有以下四项:policy、nice(2.2版内核该项为priority)、counter、rt_priority。其中,policy是进程的调度策略,其可用来区分实时进程和普通进程,实时进程优先于普通进程运行。nice从最初的UNIX沿用而来,表示进程的静态负向优先级,其取值范围为19~-20,以-20优先级最高。counter表示进程剩余的时间片计数值,由于counter在计算goodness()时起重要作用,因此,counter也可以看作是进程的动态优先级。rt_priority是实时进程特有的,表示实时优先级。
首先,linux根据调度策略policy从整体上区分实时进程和普通进程。对于policy为SCHED_OTHER的普通进程,linux采用动态优先级调,其优先级权值取决于(20-nice)和进程当前的剩余时间片计数counter之和。进程创建时,子进程继承父进程的nice值,而父进程的counter值则被分为二半,子进程和父进程各得一半。时间片计数器每次清零后由(20-nice)经过换算重新赋值。字面上看,nice是“优先级”、counter是“计数器”的意思,然而实际上,它们表达的是同个意思:nice决定了分配给该进程的时间片计数,nice优先级越高的进程分到的时间片越长,用户通过系统调用nice()或setpriority()改变进程静态优先级nice值的同时,也改变了该进程的时间片长度;counter表示该进程剩余的时间片计数值,而nice和counter综合起来又决定进程可运行的优先级权值。在进程运行过程中,counter不断减少,而nice保持相对不变;当一个普通进程的时间片用完以后,并不马上根据nice对counter进行重新赋值,只有所有处于可运行状态的普通进程的时间片都用完了以后(counter等于0),才根据nice对counter重新赋值,这个普通进程才有了再次被调度的机会。这说明,普通进程运行过程中,counter的减小给了其它进程得以运行的机会,直至counter减为0时才完全放弃对CPU的使用,这就相当于优先级在动态变化,所以称之为动态优先调度。
对于实时进程, linux 采用了两种调度策略,即 SCHED_FIFO( 先来先服务调度 ) 和 SCHED_RR (时间片轮转调度)。因为实时进程具有一定程度的紧迫性,所以衡量一个实时进程是否应该运行,采用了一个比较固定的标准,即参考 rt_priority 的值。用函数 goodness ()计算进程的优先级权值时,对实时进程是在 1000 的基础上加上 rt_priority 的值,而非实时进程的动态优先级综合起来的调度权值始终在以下,所以 goodness ()的优先级权值计算方法确保实时进程的调度权值始终比所有的非实时进程都要大,这就保证了实时进程的优先运行。实时进程的 counter 与 nice 都与其优先级权值无关,这和普通进程是有区别的,实时进程 task_struct 中的 counter 和 nice 只与 SCHED_RR 调度策略进程的时间片计数相关;而对于 SCHED_FIFO 调度策略的实时进程没有调度的参考意义。