正文:
- 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间。
- 多任务系统有两类:抢占式和非抢占式多任务。
- 进程在被抢占之前能够运行的时间是预先设置好的,叫时间片。当今的操作系统对程序运行都采用了动态时间片计算方式,并且引入了可配置的计算策略。
- 反转楼梯最后期限调度算法吸取了队列理论,将公平调度的概念引入Linux调度程序。并在2.6.23内核版本代替了O(1)调度算法。此刻它被称为完全公平调度算法。
- 调度程序总是选择时间片未用尽且优先级最高的进程运行。用户和系统可以设置进程的优先级来影响系统的调度。
- Linux采用两种不同的优先级范围,第一种是用nice(—20~19)值,nice值高的进程获得的处理器时间少;第二种范围是实时优先级(0~99),与nice值意义相反,越高的实时优先级意味着进程优先级越高。
- 时间片是一个数值,它表明进程在被抢占前所能维持运行的时间。
- Linux的CFS调度器并没有直接分配时间片到进程,而是将处理器的使用比例划分给了进程,这样进程锁获得的处理器时间和系统负载密切相关的。如果消耗的使用比比当前进程小,则新的进程立刻投入运行,抢占当前进程。
- Linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块化结构被称为调度器类,它允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程。
- 调度时进程抢占会带来一定的代价:将一个进程换出,另一个换入本身有消耗,同时还会影响缓存的效率。
- 目标迟延
- 任何进程所获得的处理器时间是由它自己和其他所有可运行进程nice值的相对值决定的。nice值对时间片的作用是几个加权。任何nice值对应的绝对时间不再是一个绝对值,而是处理器的使用比。
- 进程调度的主要入口点是函数schedule(),它正是内核其他部分用于调用进程调度器的入口:选择哪个进程可以运行,何时将其投入运行。它通常都需要和一个具体的调度类相关联,它会找到一个最高优先级的调度类(有自己的可运行队列),然后问后者谁才是下一个该运行的进程。
- 进程休眠有很多原因,但肯定是为了等待一些事件。一个进程还可能在尝试获取一个已被占用的内核信号量时被迫进入休眠。
- 进程把自己标记成休眠状态,可从红黑树中移出,放入等待队列,然后调用schedule()选择和执行一个其他进程。唤醒过程则正好相反。
- 休眠有两种相关的进程状态:Task_interruptible,task_uninterruptible.
- 休眠通过等待队列进行处理。等待队列由等待某些事件发生的进程组成的简单链表。内核用wake_queue_head_t来代表等待队列。
- 唤醒操作通过函数wake_up()进行,它会唤醒指定的等待队列上的所有进程。
- 上下文切换:从一个可执行进程切换到另一个可执行进程,由定义在/kernel/sched.c中的context_switch()负责处理。每当一个新的进程被选出来准备投入运行时,schedule()就会调用该函数。
- 每个进程描述符都包含一个need_resched标志(其实在thread_info结构体内),因为访问进程描述符内的数值要比访问一个全局变量快(因为current宏速度很快并且描述符通常都在高速缓存中)。
- 内核无论是在中断处理程序还是在系统调用后返回(会发生用户抢占的时期),都会检查need_resched标志。
- 从中断处理程序或系统调用返回的路径都是跟体系结构相关的,在enter.S文件(包含内核入口部分的程序,内核退出部分的相关代码也在其中)中实现。
- 只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。即只要没有持有锁,内核就可以进行抢占。锁是非抢占区域的标志。由于内核是支持S MP的,所以没有持有锁时,正在执行的代码就可以重新导入,进行抢占。(每个进程的thread_info中的preempt_count计数器)
- 内核抢占一般发生在:中断处理程序正在执行,且返回内核空间之前;内核代码再一次具有可抢占性的时候;如果内核中的任务显示地调用schedule();如果内核中的任务阻塞,导致调用schedule().
- Linux提供了两种实时调度策略:sched_fifo,sched_RR.(sched_normal是非实时调度策略)。
- sched_rr级的进程在耗费事先分配给它的时间后就不能再继续运行,它是带有时间片的sched_fifo.