2.3.1 O(n)调度器 - 调度逻辑
O(n) 调度器是 Linux 在 2.4 版的内核中引入的,由于调度器算法的时间复杂度为 O(n), 该调度器的名字也由此而来;本节我们基于 linux-2.4.18 来了解一下该调度器的实现逻辑。
整个O(n)调度器的实现都在文件 kernel/sched.c
中,总共只有1300行左右的代码,整个调度逻辑的代码量远少于当前版本中的任何一个调度器(例如 dl 与 rt 调度器都有接近3000行代码),因此其实现思路非常简单。
我们先来看一下O(n)调度器如何管理任务。O(n)调度器采用一个全局的运行队列来管理任务,该队列的表头保存在全局变量 runqueue_head
中。所有的就绪任务都会被放入该队列中,每次调度时,系统会遍历整个队列然后挑出最适合的任务,即优先级最高的任务来执行,因此该调度算法的时间复杂度为O(n), 这也是该调度器被叫着O(n)调度器的原因。
函数 schedule()
是调度逻辑的入口,其中选取任务的逻辑如下:
调度器通过一次遍历选取了优先级最高的任务,如果队列为空,则系统将在该CPU上运行idle task。任务的优先级通过函数 goodness()
来判断。
注意在该版本中,调度器仅支持实时任务与普通任务,实时进程的调度策略包含 SCHED_FIFO
与 SCHED_RR
, 普通进程的调度策略叫 SCHED_NORMAL
, 相当于现在的 SCHED_NORMAL
, 实时进程的优先级高于普通进程,需要优先保证其时间供给。
goodness()
在计算任务优先级时会综合考虑各种因素,以确保系统在时间分配上是公平的。 struct task_struct
中与优先级计算有关的字段包含如下几个:
我们将 nice
与 rt_priority
叫着静态优先级,因为他们是用户设置的,并且在任务运行过程中不会改变。函数 goodness()
的返回结果我们叫着动态优先级,动态优先级是基于静态优先级计算出来的,并且综合考虑了其它各种情况。其完整代码如下:
对于实时任务,系统将其静态优先级(rt_priority
)加上1000然后返回,这样能够确保所有实时任务的动态优先级都高于普通进程。而对于普通进程,系统除了考虑静态优先级(nice)之外,还会如下几个方面对优先级进行调整:
根据任务的剩余时间进行调整 如果一个任务剩余的CPU时间很多,就说明该任务之前得到的运行机会很少,可能是任务在等待某个外部事件(例如IO事件),也可能是一直被高优先级的任务排挤。为了更加公平,系统在每次调度时会将任务的剩余时间作为权重的奖励,这样越到调度后期,剩余时间更多的任务就越有机会被调度。
根据CPU进行调整 在 SMP 架构中,如果将任务 p 继续运行在其上次运行的 CPU 上的话,可能能够提升 CPU 缓存与 TLB 的命中率。
根据任务的内存空间进行调整 代码中
p->mm == this_mm
用来判断任务p与当前任务的内存地址空间是否一样,如果等式成立,则说明两个任务是同一个进程中的两个线程;而!p->mm
用来判断任务 p 是否是内核线程,所有的内核线程都共享一个内存空间。在这两种情况下切换任务的话运行效率会更高,因为进程切换时不用切换地址空间,所以调度器会为权重+1作为奖励。
Last updated