处理器调度

在多道程序系统中,进程数量往往多于处理机的个数,所以进程争用处理机的情况在所难免。而为了对进程进行处理器的分配和调度问题就显得尤为重要,操作系统对处理器的管理通过处理器调度进行实现。

调度的层次

处理器的调度分为高级调度、中级调度和低级调度。

高级调度

高级调度,又称作业调度,简而言之是内存与辅存之间的调度,主要是按一点原则从外存上处于后备状态的作业中进行挑选,并为其分配内存和I/O等资源从而建立对应进程,使其得到CPU使用权。

作业调度的执行频率较低,通常为几分钟一次。

中级调度

中级调度,又称内存调度,用于将暂时不能运行的进程调至外存等待,此时的进程状态为挂起态。当它们具备运行条件且内存又稍有空闲时,又由中级调度决定把外存上的就绪进程重新调入到内存并改为就绪态,以此提高内存利用率和系统吞吐量。

低级调度

低级调度,又称进程调度,主要是通过某种方法策略从就绪队列选取一个进程为其分配CPU。进程调度是最基本的,不可或缺。

进程调度的频率很高,通常几十毫秒一次。

相关评价准则

CPU 利用率

CPU利用率=CPU有效工作时间CPU有效工作时间+CPU空闲等待时间CPU利用率=\frac{CPU有效工作时间}{CPU有效工作时间+CPU空闲等待时间}

系统吞吐量

系统吞吐量表示单位时间内 CPU 完成作业的数量
通常,长作业需要消耗较长的处理机时间,所以会降低系统吞吐量;而短作业则提高吞吐量。

时间准则

  • 提交时间:作业提交给操作系统的时间点
  • 开始时间:作业真正开始执行的时间点
  • 服务时间:作业需要在处理机中运行的总时间
  • 完成时间:作业完全处理完毕的时间点
  1. 完成时间 = 开始时间 + 服务时间
  2. 周转时间 = 完成时间 - 提交时间
  3. 带权周转时间 = 周转时间 / 服务时间
  4. 等待时间 = 开始时间 - 提交时间 = 周转时间 - 运行时间
  5. 响应比 = (等待时间 + 服务时间) / 服务时间 = 等待时间/服务时间 + 1

调度的实现

调度器

调度程序的结构

上图给出了调度程序(即调度器)的结构示意图。其中特别需要注意的是上下文切换器
上下文切换器在对处理机进行切换时,需要大量 load 和 store 命令。会发生两对上下文的切换操作:

  1. 将当前进程的上下文保存到其 PCB 中,再装入分派程序的上下文,以便分派程序运行;
  2. 移出分派程序的上下文,将新选进程的 CPU 现场信息装入处理机的各个相应寄存器中。

调度的时机

需要进行进程调度与切换的情况

  • 当前运行的进程主动放弃处理机

    • 进程正常终止
    • 运行过程中发生异常而终止
    • 进程主动请求阻塞(如等待I/O)

    有的系统中,只允许进程主动放弃处理机

  • 当前运行的进程被动放弃处理机

    • 分给进程的时间片用完
    • 有更紧急的事需要处理( 如I/O中断)
    • 有更高优先级的进程进入就绪队列

    有的系统中,进程可以主动放弃处理机,当有更紧急的任务需要处理时,也会强制剥夺处理机(被动放弃)

不能进行进程调度与切换的情况

  1. 在处理中断的过程中。中断处理过程复杂,与硬件密切相关,很难做到在中断处理过程中进行进程切换。
  2. 进程在操作系统内核程序临界区中。
  3. 在原子操作过程中(原语)。原子操作不可中断,要一气呵成(如之前讲过的修改PCB中进程状态标志,并把PCB放到相应队列)

✅进程在操作系统内核程序临界区中不能进行调度与切换
❌进程处于临界区时不能进行处理机调度

进程切换与过程

狭义的进程调度进程切换的区别:

  • 狭义的进程调度指的是从就绪队列中选中一个要运行的进程

这个进程可以是刚刚被暂停执行的进程,也可能是另一个进程,后一种情况就需要进程切换

  • 进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程

广义的进程调度包含了选择一个进程和进程切换两个步骤

进程切换的过程主要完成

  1. 对原来运行进程各种数据的保存
  2. 对新的进程各种数据的恢复

⚠️进程切换是有代价的,因此如果过于频繁的进行进程调度、切换,必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,而真正用于执行进程的时间减少。

闲逛进程

在进程切换中,如果系统中没有就绪进程,就会调度 闲逛进程(idle) 运行。
如果没有其他进程就绪,该进程就一直运行,并且在运行过程中测试中断。
闲逛进程的优先级最低,不需要CPU之外的资源,它不会被阻塞

调度方式

进程调度又可分为 抢占式非抢占式 两种(或剥夺式和非剥夺式)。

所谓非抢占式就是当进程正在运行时,它就会一直运行,直到该进程完成或发生某个事件发生而被阻塞时,才会把 CPU 让给其他进程。

对应的,抢占式就是当进程正在运行的时,可以被打断,把 CPU 让给其他进程。

常见进程调度算法

先到先服务FCFS

先到先服务调度算法(First Come First Serve,FCFS):按照进程到达的先后顺序进行调度,先到的进程就先被调度,也就是说,等待时间越久的越优先得到服务。该算法既可用于作业调度,也可用于进程调度,基本思想是一致的。

下面给出一实例加以说明:

进程到达时间运行时间
P1P_107
P2P_224
P3P_341
P4P_454

根据其到达时间进行排序,以运行时间作为长度,可以用如下甘特图表示:

FCFS-甘特图

显而易见,FCFSFCFS 算法并不会强制让P1P_1终止运行,所以FCFSFCFS非抢占式 的,此外我们还能得到如下结论:

优点:算法实现简单,利于长作业(CPU繁忙型)工作

缺点:不利于短作业(I/O繁忙型),排在长进程后面的短进程需要等待很长时间

短作业优先SJF

最短作业/进程优先调度算法(Shortest Job First,SJF):每次调度时选择当前已到达的、且运行时间最短 的进程

思路很简单,继续用上文中的实例进行计算。

SJF甘特图

首先着眼于 “当前已到达”,显然只有P1P_1在0时刻达到,于是在运行队列中放入P1P_1,得到{P1}\{P_1\}

由于P1P_1运行需要的时间是7,运行结束之后时间点为7,此时P2P3P4P_2、P_3、P_4已经到达,此时再着眼于 “运行时间最短”,将它们进行排序得到P3,P2,P4P_3,P_2,P_4.

最终即可得到运行队列:{P1,P3,P2,P4}\{P_1,P_3,P_2,P_4\}.

于是我们就可以通过时间准则计算相关数据了,此处不再赘述。

可以证明SJFSJF 调度算法是最优的
对于给定的一组进程,该算法的平均等待时间、平均周转时间最小

拓展内容

SJFSJF算法是"最优"的调度,但难点在于如何预测进程的执行时间(Burst Time)

对于批处理系统中的长期调度来说,可以将用户提交进程时输入的执行时间上限作为依据。

但对于短期调度来说,没有办法能够提前得知下一个要被分配CPU的进程的执行时间长短,因此只能通过历史数据来进行预测,公式如下:

τn+1=αtn+(1α)τn\tau_{n+1}=\alpha t_n+(1-\alpha)\tau_n

tnt_n 包括最近信息,而τnτ_n 存储了过去的历史信息。参数αα 控制最近和过去历史在预测中的权重

  • 如果α=0α = 0,那么τn+1=τnτ_{n+1} = τ_n,最近历史没有影响(当前情形为瞬态);

  • 如果α=1α = 1,那么τn+1=tnτ_{n+1} = t_n,只有最近 CPU 执行才重要(过去历史被认为是陈旧的、无关的)

  • 更为常见的是α=0.5α = 0.5,这样最近历史和过去历史同样重要。

  • 初始值可作为常量或系统的总体平均值。


易知SJF/SPFSJF/SPF算法也是 非抢占式 的,它降低了平均等待时间,提高了吞吐量。

SJF/SPFSJF/SPF算法和FCFSFCFS算法恰好相反,它对长程不利。如果一直有短进程到来,那么长进程永远得不到调度,长进程有可能会饿死,处于一直等待短作业执行完毕的状态。

此外,此算法未考虑作业/进程的优先紧迫程度,不能用于实时系统。

高响应比优先HRRN

高响应比优先算法(Highest Response Ratio Next,HRRN):只有当前运行的进程主动放弃 CPU 时(正常/异常完成,或主动阻塞),才需要进行调度,调度时计算所有就绪进程的响应比,为响应比最高的进程分配 CPU

响应比Rp=等待时间+服务时间服务时间响应比R_p = \frac{等待时间 + 服务时间}{服务时间}

通过对响应比的分析可知:当等待时间相同时,服务时间越短响应比越高,所以此时 HRRN 算法类似于 SJF 算法;当服务时间相同时,等待时间越短响应比越高,所以此时 HRRN 算法类似于 FCFS 算法。


对于长作业来说,随着等待时间的增加其响应比会相应地提高,所以克服了“饥饿”现象。
但 HRRN 算法每次都需要计算每个作业的响应比,增加了系统的开销。

轮转调度RR

轮转法(Round-Robin Scheduling,RR),又称时间片调度算法:系统将所有就绪进程按 FIFO 规则排队,按一定的时间间隔(即时间片,通常为10ms~200ms)把CPU分配给队列中的进程。示例图如下:

RR示意图

该算法对每个进程都一视同仁,就好比大家都排好队,一个一个来,每个人都运行一会儿再接着重新排队等待运行。

需要注意的是:时间片的长度是一个很关键的因素。通常由 系统响应时间、就绪队列中的进程数目和系统处理能力等因素确定。

  • 如果时间片设置得太短,就会导致频繁的进程上下文切换,降低了 CPU 效率;

  • 如果时间片设置得太长,那么随着就绪队列中进程数目的增加,轮转一次消耗的总时间加长,即每隔进程的相应速度放慢。甚至时间片大到让进程足以完成其所有任务,RRRR 调度算法便退化成FCFSFCFS 算法。

不难发现,RRRR调度算法属于 抢占式

最短剩余时间优先SRTN

最短剩余时间优先(Shortest Remaining Time Next,SRTN)算法是最短作业优先的抢占式版本

当一个新的进程到达时,把它所需要的整个运行时间与当前进程的剩余运行时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程,否则新的进程等待

再次使用上文中提到的实例进行计算。

SRTN算法示例1

SRTN算法示例2

SRTNSRTN调度算法属于 抢占式

最高优先级HPF

RR 调度算法对所有的进程都是相同的策略,如果用户进程太多,可能会导致内核的服务进程响应跟不上。而在操作系统中,内核进程是比用户进程重要的多的,毕竟它关乎整个系统的稳定性。

最高优先级调度算法(Highest Priority First,HPF)就是从就绪队列中选择最高优先级的进程进行运行

进程的优先级分为静态优先级或动态优先级:

  • 静态优先级:创建进程时候,就预先规定优先级,并且整个运行过程中该进程的优先级都不会发生变化。一般来说,内核进程的优先级都是高于用户进程的。
  • 动态优先级:根据进程的动态变化调整优先级。比如随着进程的运行时间增加,适当的降低其优先级;随着就绪队列中进程的等待时间增加,适当的升高其优先级。

另外,需要注意的是,最高优先级算法并非是固定的抢占式策略或非抢占式,系统可预先规定使用哪种策略

  • 非抢占式:当就绪队列中出现优先级高的进程,则运行完当前进程后,再选择该优先级高的进程。
  • 抢占式:当就绪队列中出现优先级高的进程,则立即强制剥夺当前运行进程的 CPU 资源,分配给优先级更高的进程运行。

一般来说,进程的优先级有以下原则的参考:

  1. 系统进程 > 用户进程
  2. 交互进程 > 非交互进程(或 前台进程 > 后台进程)
  3. I/O型进程 > 计算型进程

多级反馈轮转RRMF

多级反馈轮转法(Round Robin with Multilevel Feedback,RRMF)又称多级反馈队列调度( Multilevel Feedback Queue Scheduling),是集合了RRRR算法和HPFHPF算法的调度算法,可以很好地兼顾多方面的系统目标。

算法思想

  1. 设置NN 个就绪队列(Q1,...,QNQ_1,...,Q_N),其中各个队列对于CPUCPU的优先级不同,一般有:

    Priority(Qi)>Priority(Qj),i>jPriority(Q_i)>Priority(Q_j),i>j.

  2. 为每一个就绪队列QiQ_i 都分配相应的时间片TiT_i,且优先级越高的队列被分配到的时间片越小.

  3. QNQ_N 内的进程遵循 时间片轮转法则(即RRRR),而其余队列遵循FCFSFCFS法则.

  4. 为当前已经等待被执行的各个进程分配好就绪队列

以上即为算法的基本规则和准备,下面给出算法的具体运作流程

  1. 当一个进程PnewP_{new} 进入待调度队列等待时,首先将其放入当前优先级最高的队列QiQ_i ,放入方式遵循FIFOFIFO原则,放在已有进程的后面;
  2. 对于任意Qi,iNQ_i,i\neq N 队列,以其对应时间片TiT_i 作为每个进程的最大执行时间段,按照FCFSFCFS算法思想依次运行该队列的进程;
  3. 当轮到某进程执行时,如果它能够在该队列规定的时间片内完成,则直接执行,否则执行完对应时间片后将其改放入下一队列,即Qi+1Q_{i+1} 队列内(当然同样是放在队列末尾,FIFOFIFO)等待被执行;
  4. 当某一队列为空,即isEmpty(Qi)=TrueisEmpty(Q_i)=True 时,系统才开始对下一队列Qi+1Q_{i+1} 进行FCFSFCFS调度,依次类推直到被降至QNQ_N 时,才按照RRRR算法调度;
  5. 当系统正在执行QiQ_i 中的进程pp ,而此时又有新的进程PnewP_{new} 被放入比之优先级更高的队列Qj,j>iQ_j,j>i中时,新进程将会抢占正在执行的 CPU,使得 CPU 转而先去对队列QjQ_j 处理,而这时pp 则被放入QiQ_i 的末尾等待执行。

实例演示

再次使用上文中提到的实例进行演示。

首先我们先假设有如下规定

演示规定
  1. 有三个优先级队列Q1,Q2,Q3Q_1,Q_2,Q_3,优先级依次减小;
  2. 在上文中提到的进程P1P_1到来前已有几个进程(用小写字母表示)在等待CPU执行,其分配如下:

RRMF前期设置

  1. t=0t=0时刻,即P1P_1到来时,CPU执行到进程cc处,且已执行了3个单位时间

调度算法开始执行,步骤如下:

首先,P1P_1到来,所以将其放置在Q1Q_1处;

因为T2=4>3T_2=4>3,即进程cc并没有执行完毕,所以需要将剩下Δt=43=1\Delta t=4-3=1cc放置于Q2Q_2末尾,CPU转而执行Q1Q_1

RRMF算法流程1

t=2t=2时刻,位于Q1Q_1P1P_1并没有在规定时间片T1=3T_1=3内执行完毕,所以此时到达的P2P_2置于Q1Q_1末尾等待执行;

RRMF算法流程2

P1P_1执行完3个单位时间后,由于还剩下Δt1=73=5\Delta t_1=7-3=5的需要运行的时间,所以将P1P_1调整到Q2Q_2末尾,执行P2P_2

P2P_2执行了1个单位时间后,到了t=4t=4时刻,此时P3P_3到场,放置在末尾;

P2P_2执行了2个单位时间后,到了t=5t=5时刻,此时P4P_4到场,放置在末尾;

RRMF算法流程3

P2P_2执行了3个单位时间后,到了t=6t=6时刻,时间片结束,还剩下Δt2=43=1\Delta t_2=4-3=1的需要运行的时间,所以将P2P_2调整到Q2Q_2末尾,执行P3P_3

P3P_3执行了1个单位时间后,到了t=7t=7时刻,此时P3P_3任务完成,转而执行P4P_4

P4P_4执行了3个单位时间后,到了t=10t=10时刻,时间片结束,还剩下Δt4=43=1\Delta t_4=4-3=1的需要运行的时间,所以将P4P_4调整到Q2Q_2末尾,此后Q1Q_1为空,CPU转而去执行Q2Q_2队列;

RRMF算法流程4

RRMF算法流程5

到了t=10+4+4+1=19t=10+4+4+1=19时刻,CPU将a,b,c也完成后,继续执行P1P_1

RRMF算法流程6

P1P_1执行了4个单位时间后,到了t=19+4=23t=19+4=23时刻,时间片结束,还剩下Δt1=54=1\Delta t_1=5-4=1的需要运行的时间,所以继续将P1P_1放置在Q3Q_3末尾;

到了t=23+1+1=25t=23+1+1=25时刻,CPU将P2P_2P4P_4也完成后,转而对Q3Q_3队列进行处理。

RRMF算法流程7

优缺点

多级反馈队列的优势有以下几点:

  1. 终端型作业用户:短作业优先;
  2. 短批处理作业用户:周转时间较短;
  3. 长批处理作业用户:经过多队列的部分执行,不会导致长时间得不到处理

总结与参考

FCFSSJFHRRNRRRRMF
中文名称先到先服务短作业优先高响应比优先时间片轮转多级反馈队列
抢占情况支持不可抢占皆可皆可只能抢占不一定
优点公平、简单效率最高兼顾长短作业兼顾长短作业兼顾长短作业、响应时间好、可行性强
缺点不利于短作业长作业会饥饿计算响应比开销大平均等待时间长,上下文切换浪费时间
适用于作业调度、批处理系统分时系统相当通用
默认决策模式非抢占非抢占非抢占抢占抢占

  1. 图解进程调度算法|CS-Wiki,GitHub
  2. 操作系统的进程调度算法总结|简书
  3. 进程调度算法|CSDN
  4. 【操作系统】CPU调度算法|从0开始数,Bilibili