向下之旅(六):进程调度(二)

  抢占和上下文切换

  上下文切换,也就是从一个可执行进程切换到另一个可执行进程,通过context_switch()函数负责处理。每当一个新的进程被选出来准备运行的时候,schedule()就会调用该函数。主要步骤如下:

  1.调用switch_mm,该函数负责把虚拟内存从上一个进程映射切换到新进程中。

  2.调用swicth_to,该函数负责从上一个进程的处理器切换到新进程的处理器状态。包括保存、恢复栈信息和寄存器信息。

  内核提供了一个need_resched标志来标明是否需要重新执行一次调度。当某个进程耗尽它的时间片时,sechduler_tick()就会设置这个标志;当一个优先级高的进程进入可执行状态的时候,try_to_wake_up()也会设置这个标志。

  每个进程都包含一个need_resched标志,这是因为访问进程描述符内的数值要比访问一个全局变量快(因为current宏速度很快并且描述符通常都在高速缓存中)。在2.2以前的内核版本中,它是一个全局变量,在2.2-2.4版本的时候,它在task_struct结构中,在2.6版本中,它在thread_info结构体中,用一个特别的标志变量中的一位来标示。

  用户抢占

  内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。用户抢占发生在:

  1.从系统调用返回用户空间。

  2.从中断处理程序返回用户空间。

  内核抢占

  一般的操作系统不同,Linux完整的支持内核抢占,在不支持内核抢占的内核中,内核代码可以一直执行,到它完成为止。调度程序没有办法在一个内核级任务正在执行的时候重新调度——内核中的各任务是协作方式调度的,不具备抢占性。

  为了支持内核抢占,为每个进程的thread_info引入了preempt_count计数器。该计数器初始值为0,每当使用锁的时候数值加1,释放所的时候数值减1.当数值为0的时候,内核就可执行抢占。从中断返回内核空间的时候,内核会检查need_resched和preempt_count的值。如果need_resched被设置,并且preempt_count为0的话,此时内核可以被抢占。或者内核中的进程被阻塞,或者显示的调用了schedule(),也会发生内核抢占。几种情况如下:

  1.当从中断处理程序正在执行,且返回内核空间之前

  2.当内核代码再一次具有可抢占性的时候

  3.如果内核中的任务显示的调用schedule()

  4.如果内核中的任务阻塞(这样也会导致调用schedule())

  实时

  Linux提供了两种实时调度的策略:SCHED_FIFO和SHCED_RR。普通的为SCHED_NORMAL。SCHED_FIFO实现了一种简单的,先入先出的调度算法,不使用时间片。SCHED_FIFO级的进程会比任何SCHED_NORMAL级的进程都先得到调度。一旦一个SCHED_FIFO级进程处于一个可执行的状态,就会一直执行,直到它自己受阻塞或显示的释放处理器为止,不基于时间片,可以一直执行下去。只有较高级的SCHED_FIFO或者SCHED_RR任务才能抢占SCHED_FIFO任务。其余的只能等到它执行完之后才有机会执行。

  实时优先级范围从0到MAX_RT_PRIO减1,。默认的情况下,MAX_RT_PRIO为100——即默认的实时优先级范围从0到99、SCHED_NORMAL级进程的nice值共享了这个取值空间,它的取值范围是MAX_RT_PRIO到MAX_RT_PRIO+40.也就是说,在默认的情况下,nice值从-20到19直接对应的是100到139的实时优先级范围。

  与调度相关的系统调用

  

 


参考自:《Linux Kernel Development》.

posted on 2016-03-15 15:38  画家丶  阅读(164)  评论(0)    收藏  举报