跳转至

4 Scheduling

约 2316 个字 预计阅读时间 8 分钟

​多道 (multiprogramming) 环境下,进程的个数通常大于 CPU 的个数。CPU 调度就是 OS 关于哪个 ready 进程可以运行(使用 CPU)以及运行多久的决定。

​其目标是始终允许某个进程运行以最大化 CPU 利用率,同时保证一定的公平性。这在多道环境下是必要的,关系到系统的整体效率。

4.1 调度的时机

​​​CPU 调度可能出现在 任意 一个进程:

a. Running -> Waiting,如等待 I/O
b. Running -> Terminated
c. Running -> Ready,当发生了 interrupt,如计时器到时间了
d. Waiting -> Ready,如 I/O 完成了
e. New -> Ready

​非抢占式 (nonpreemptive) 调度只会在 a, b 处进行调度,因为只有这时候当前正在运行的进程不能再运行了;抢占式 (preemptive) 调度会发生在上述任何时机(例如,当一个新的进程被创建,即 e 发生时,也会重新进行调度)。因此,抢占式调度使得 OS 有更充分的控制,但它也更复杂。

​也就是说,当上述情况发生时,调度器会被调用,来进行调度。

4.2 调度的过程

​这个过程叫 上下文切换 (context switch),这里的上下文其实就由 PCB 来表示。

4.3 调度算法的评价

对于不同的应用场景,我们对 scheduling 的要求也有所不同,有些也会有冲突。为此,我们需要充分考虑各种算法的属性。我们可能需要的要求包括:

  • Maximize CPU Utilization : CPU 使用率,即 CPU 非空闲的时间比例
  • Maximize Throughput : 吞吐量,每个时间单元内完成的进程
  • Minimize Turnaround Time : 周转时间,从进程创立到进程完成的时间,包括等待进入内存、在 ready queue 中等待、在 CPU 上执行、I/O 执行等时间
  • Minimize Waiting Time : 等待时间,在 ready queue 中(或在 Ready 状态下)等待所花的时间之和
  • Minimize Response Time : 响应时间,交互系统从进程创立到第一次产生响应的时间

这些要求有时甚至是冲突的。例如,较多的 context switch 会减少 throughput,因为 context switch 过程中并没有有用的工作;而较少的 context switch 会增加 response time。

4.4 调度算法

基于上述不同目的,多种调度算法被设计出来。

4.4.1 ​First-Come First-Served (FCFS) | Nonpreemptive

​先申请 CPU 的进程首先获得 CPU。可以用一个 FIFO 队列简单实现。

​​

​Convoy effect - short process behind long process,​尤其是当有进程进入 waiting 后,再返回的时候又需要重新排队。

4.4.2 ​Shortest-Job-First (SJF)

SJF 的核心想法是,让下一次运行时间最短的进程先来运行;根据数学知识,我们可以得知这样能得到最少的平均等待时间。

Note

当然,如果我们不忽略非抢占式系统中进程 running -> waiting 的情况,则「下一次运行时间」也就是剩余运行时间了。所以,如果出现了不忽略的情况,大家也要会处理。核心就是把握调度的时机。可以通过下面两个例子理解。

对于非抢占式的系统来说,当我们忽略 I/O 等会进入 waiting 的情况(因为题目通常这样设计),进程「下一次运行时间」就是整个进程所需的总运行时间。但是,对于抢占式的系统而言,「下一次运行时间」实际上是进程的剩余运行时间,因为进程可能曾经被打断过。因此我们将 SJF 进一步细分成了两种。

​Non-preemptive: Shortest-next-CPU-burst

​每当 CPU 调度时,其选取 ready queue 中下次 CPU 执行时间最短的进程。这样会使得给定的一组进程具有 minimum average waiting time.

  • 在这个情景中,0s 时只有 P1 到达,因此 P1 先运行
  • 由于是非抢占式的,因此 P1 运行过程中其他进程的到达并不会导致重新调度,P1 得以完全运行
  • P1 结束时,剩余进程都已到达,处于 ready 状态,因此调度器从 ready queue 中选取 brust time 最短的来运行,以此类推。

​Preemptive: Shortest-remaining-time-first

​每当 CPU 调度时(注意抢占式调度的调度时机),选择最短剩余运行时间的进程。

  • 在这个情境中,0s 时只有 P1 到达,因此 P1 先运行
  • 但不同的是,由于是抢占式的,因此 2s P2 到达时也会引发一次调度,此时 P1 的剩余时间是 8s,P2 是 6s,因此 P2 优先运行
  • 4s 时 P3 到达也引发一次调度,但此时 P1 的剩余时间是 8s,P2 是 4s,P3 是 7s,其中 P2 最短,因此仍然是 P2 继续运行
  • 5s 时 P4 到达也引发一次调度,此时 P1 的剩余时间是 8s,P2 是 3s,P3 是 7s,P4 是 2s,其中 P4 最短,因此 P4 优先运行
  • P4 运行结束时,ready queue 中 P1 的剩余时间是 8s,P2 是 3s,P3 是 7s,因此 P2 先运行,以此类推。

由此也可以看出,两个版本的调度选择其实没有太大的区别,最大的区别只是调度的时机不同。

如我们之前所述,​SJF 的两个版本都可以获得最小的平均等待时间,但最大的问题在于我们并不知道「下一次运行时间」。解决方案是预测,将下次执行时间预测为此前 CPU 执行长度的指数平均。指数平均需要操作系统统计该进程此前的运行情况;关于指数平均的具体实现,感兴趣的同学可以在课本中自行查阅。

4.4.3 ​Round-Robin (RR) | Preemptive

​定义一个 时间片 (time slice / time quantum) ,即一个固定的较小时间单元 (10-100ms)。除非一个 process 是 ready queue 中的唯一进程,它不会连续运行超过一个时间片的时间。Ready queue 是一个 FIFO 的循环队列。每次调度时取出 ready queue 中的第一个进程,设置一个计时器使得进程在一个时间片后发生中断,然后 dispatch the process。

​相比 SJF 而言,平均等待时间更长,但响应时间更短。

​RR scheduling 的性能很大程度上取决于时间片的大小。如果时间片较小,则 response/interactivity 会很好,但会有较大的 overhead,因为有较多的 context-switch;时间片较大则响应较差,但 overhead 会较小。如果 时间片 \(\to \inf\),则 RR \(\to\) FCFS。

​在实践中,时间片大约 10~100ms,每次 contest-switch 约 10μs。即 context-switch 的时间花费是比较小的。

4.4.4 ​Priority Scheduling

​每个进程都有一个优先级,每次调度时选取最高优先级的进程。(下例中规定优先级值小的优先级高。)

​优先级可以是内部的或者外部的:

  • internal: 一些测量数据,例如 SJF 是 Priority 的一个特例,即优先级由预测 CPU 运行时间决定。
  • external: 由用户指定进程的重要性。

​Priority Scheduling 也可以与 Round-Robin 结合,如:

​要实现 Priority Scheduling,可以简单地将 ready queue 用 priority queue 实现;priority queue 也可以是抢占式或非抢占式的,如 SJF 一样。

​Priority 的一个重要问题是 indefinite blocking / starvation ,即低优先级的进程可能永远没有机会被执行。一个解决方法是 Priority Aging ,即根据等待时间逐渐增加在系统中等待的进程的优先级。

4.4.5 ​Multilevel Queue Scheduling

​在实际应用中,进程通常被分为不同的组,每个组有一个自己的 ready queue,且每个队列内部有自己独立的调度算法。

例如,前台队列使用 RR 调度以保证 response,后台队列可以使用 FCFS。

同时,队列之间也应当有调度。通常使用 preemptive priority scheduling,即当且仅当高优先级的队列(如前台队列)为空时,低优先级的队列(如后台队列)中的进程才能获准运行。

也可以使用队列间的 time-slicing,例如一个队列使用 80% 的时间片而另一个使用 20%。

例如:

4.4.6 ​Multilevel Feedback Queue Scheduling

​Multilevel Feedback Queue Scheduling 允许进程在队列之间迁移。这种算法可以有很多种实现,因为队列的数量、每个队列中的调度策略、队列之间的调度算法以及将进程升级到更高优先级/降级到更低优先级的队列的条件都是可变的。一个系统中的最优配置在另一个系统中不一定很好。这种算法也是最为复杂的。

​看这样一个例子:有三个队列 0, 1, 2,优先级逐次降低。当进程 ready 时被添加到 Q0 中,Q0 内部采用 RR Scheduling,的每个进程都有 8ms 的时间完成其运行,如果没有完成则被打断并进入 Q1;只有当 Q0 为空时 Q1 才可能被运行。Q1 内部也使用 RR Scheduling,每个进程有 16ms 时间完成其运行,如果没有完成则被打断并进入 Q2;只有当 Q1 也为空时 Q2 才可能被运行。Q2 内部采用 FCFS 算法。

颜色主题调整

评论区~

有用的话请给我个赞和 star => GitHub stars
快来跟我聊天~