对于单处理器系统,每次只允许一个进程运行,任何其他进程必须等待,直到CPU空闲能被调度为止,多道程序的目的是在任何时候都有某些进程在运行,以使CPU使用率最大化。
CPU的成功调度依赖于进程的如下属性:进程执行由CPU 执行和I/O等待周期。
进程在这两个状态之间切换,进程执行从 CPU 区间开始,在这之后是 I/O 区间,接着是另一个 CPU 区间,然后是另一个I/O区间,如此进行下去,最终,最后的CPU 区间通过系统请求终止执行。
每当CPU空闲时,操作系统就必须从就绪队列中选择一个进程来执行,进程选择由短期调度程序 或 CPU 调度程序执行,调度程序从内存中选择一个能够执行的进程,并为之分配CPU。
就绪队列不必是先进先出(FIFO)队列。正如研究各种调度算法时将看到的,就绪队列可实现为FIFO队列、优先队列、树或简单的无序链表,不过,从概念上讲,就绪队列内的所有进程都要排队以等待在CPU上运行,队列中的记录通常为进程控制块。
CPU 调度处理是从就绪队列中选择进程并为之分配CPU的问题。
先到先服务(first-come, first-served(FCFS))
采用这种方案,先请求CPU的进程先分配到CPU,FCFS 策略可以用FIFO队列来实现。当一个进程进入到就绪队列,其PCB链接到队列的尾部,当CPU空闲时,CPU分配给位于队列头的进程,接着该运行进程从队列中删除。采用 FCFS策略的平均等待时间通常是比较长的。
FCFS 调度算法是非抢占的,一旦CPU被分配给了一个进程,该进程就会保持CPU直到释放CPU为止,即程序终止或是请求 I/O. FCFS 算法对于分布式系统(每个用户需要定时地得到一定的CPU时间)是特别麻烦的。允许一个进程保持CPU 时间过长将是个严重的错误。
最短作业优先调度算法(shortest-job-first (SJF) scheduling algorithm)。
这一算法将每个进程与其下一个CPU区间段相关联,当CPU为空闲时,他会赋给具有最短CPU区间的进程。如果两个进程具有相同长度,那么可以使用FCFS调度来处理。
SJF 调度算法可以证明为最佳的,这是因为对于给定的一组进程,SJF 算法的平均等待时间最小,通过将短进程移到长进程之前,短进程等待时间的减少大于长进程等待时间的增加,因而,平均等待时间减少了。
SJF 算法可以作为通用优先级调度算法的一个特例。每个进程都有一个优先级与其关联。具有最高优先级的进程会分配到CPU,具有相同优先级的进程按FCFS顺序。SJF算法属于简单优先级算法,其优先级(p)为下一个(预测的)CPU区间的倒数,CPU区间越大,则优先级越小,反之亦然。
优先级可通过内部或外部方式来定义,内部定义优先级使用一些测量数据以计算进程优先级。例如,时间极限,内存要求,打开文件的数量和平均I/O区间与平均CPU区间之比都可以用于计算优先级。
优先级调度算法的一个主要问题是无阻塞 或饥饿,可以运行但缺乏CPU的进程可以认为是阻塞的,它在等待CPU。
轮转法(round-robin, RR)调度算法是专门为分时系统设计的,它类似于FCFS调度,但是增加了抢占以切换进程。定义一个较小时间单元,称之为时间片, 时间片通常为 10 - 100 ms, 将就绪队列作为循环队列。CPU 调度程序循环就绪队列,为每一个进程分配不超过一个时间片的CPU。
为了实现RR调度,将就绪队列保存为进程的FIFO队列,新进程增加到就绪队列的尾部,CPU调度程序从就绪队列中选择第一个程序,设置定时器在一个时间片之后中断,再分派该进程。
接下来将可能发生两种情况,进程可能只需要小于时间片的CPU区间,对于这种情况,进程本身会自动释放CPU,调度程序接着处理就绪队列的下一个进程,否则,如果当前运行进程的CPU区间比时间片要长,定时器会中断并产生操作系统中断,然后进程上下文切换,将进程加入到就绪队列的尾部,接着CPU调度程序会选择就绪队列中的下一个进程。
RR调度算法是可抢占的,对于RR调度算法,队列中没有进程被分超过一个时间片的CPU时间(除非它是唯一可运行的进程),如果进程的CPU区间超过了一个时间片,那么该进程会被抢占,而被放回就绪队列。
RR算法的性能很大程度上依赖域时间片的大小,在极端情况下,如果时间片非常大,那么RR算法与FCFS算法一样,如果时间片很小(如毫秒),那么RR算法称为处理器共享,(从理论上来说)n个进程对于用户都有它自己的处理器,速度为真正处理数度的
1/n。这种方法用在 Control Data Corporation(CDC)的硬件上,可以用一组硬件和10组寄存器实现 10
个外设处理器。硬件为一组寄存器执行一个指令,然后为下一组执行,这种循环不断进行,形成10个慢处理器器而不是1个块处理器。
多级队列调度算法将就绪队列分成多个独立队列,根据进程的属性,如内存大小,进程优先级、进程类型、一个进程被永久地分配到一个队列,每个队列有自己的调度算法。这种算法的优点是低调度开销,缺点是不够灵活。
多级反馈队列调度算法,允许进程在队列之间移动,主要思想是根据不同CPU区间的特点以区分进程,如果进程使用过多的CPU时间,那么它会被移到更低的优先级队列。
之前讨论的是单处理器系统内的CPU 问题,如果有多个CPU,则负载分配成为可能,但调度问题也相应的变得更加复杂。与单处理器中的CPU调度算法一样,没有最好的解决方案。
在一个多处理器中,CPU调度的一种方法是让一个处理器(主服务器)处理所有的调度决定、I/O处理以及其他系统活动,其他的处理器只执行用户代码。这种非对称多处理方法更为简单,因为只有一个处理器访问系统数据结构,减轻了数据共享的需要。
另一种方法是使用对称处理器方法,即每个处理器自我调度,所有进程可能处于一个共同的就绪队列中,或每个处理器都有它自己的私有就绪队列。无论如何,调度通过每个处理器检查共同就绪队列并选择一个进程来执行。如果多处理器试图访问和更新一个共同数据结构,那么每个处理器必须编程:必须保证两个处理器不能选择同一个进程,且进程不会从队列中丢失。
考虑一下,当一个进程在一个特定处理器上运行时,缓存中会发生什么?进程最近访问的数据进入处理器缓存,结果是进程所进行的连续内存访问通常在缓存中得以满足。现在可以考虑一下,如果进程移到其他处理器上时,会发生什么:被迁移的第一个处理器的缓存中的内容必须为无效,而将要迁移的第二个处理器的缓存需要重新构建。由于使缓存无效或重新构建的代价高,绝大多数SMP系统试图避免将进程从一个处理器移至另一个处理器,而是努力使一个进程在同一个处理器上运行,这被称为处理器亲和性,即一个进程需要对其运行所在的处理器的亲和性。
负载平衡
设法将工作负载平均地分配到SMP系统中的所有处理器上,值得注意的是,负载平衡通常只是对那些拥有自己私有的可执行进程的处理器而言是必要的。在具有共同队列的系统中,通常不需要负载平衡,因为一但处理器空闲,它立刻从共同队列中取走一个可执行进程,但同样值得注意的是,在绝大多数支持SMP中,每个处理器都具有一个可执行进程的私有队列。
负载平衡常会抵消处理器的亲和性的优点。