假设你正在构建一个操作系统,并且希望用户能够同时运行多个程序。然而,你没有高级的多核处理器,所以你的 CPU 一次只能运行一条指令!

幸运的是,你是一位非常聪明的操作系统开发人员。你意识到可以通过让进程轮流使用CPU来模拟并行性。如果你在各个进程之间循环,并从每个进程中运行几条指令,那么它们都可以保持响应,而不会有任何一个进程独占 CPU。

但是,如何从程序代码中夺回控制权以切换进程呢?经过一番研究,你发现大多数计算机都配有定时器芯片。你可以编程定时器芯片,让它在一定时间后触发切换到操作系统中断处理程序。

硬件中断

之前,我们讨论了软件中断是如何用于将控制权从用户态程序交给操作系统的。它们被称为“软件”中断,因为它们是由程序自愿触发的——在正常的取指执行周期中,由处理器执行的机器代码会指示它将控制权切换到内核。

第 2 章 - 时间切片 - 图1

操作系统调度程序使用定时器芯片(如可编程间隔定时器)来触发硬件中断以实现多任务处理:

  1. 在跳转到程序代码之前,操作系统设置定时器芯片,使其在一段时间后触发中断。
  2. 操作系统切换到用户模式,并跳转到程序的下一条指令。
  3. 当定时器到期时,它会触发硬件中断以切换到内核模式并跳转到操作系统代码。
  4. 操作系统现在可以保存程序停止的位置,加载另一个程序,并重复这个过程。

这被称为抢占式多任务处理;中断一个进程被称为抢占)。如果你正在浏览器上阅读这篇文章,同时在同一台机器上听音乐,你的计算机很可能每秒钟以数千次的频率在遵循这个循环。

时间片计算

时间片是操作系统调度程序允许进程运行的持续时间,然后进行抢占。选择时间片的最简单方法是为每个进程分配相同的时间片,可能在10毫秒左右,然后按顺序循环任务。这被称为固定时间片轮转调度。

旁注:有趣的术语小知识!

你知道时间片通常被称为“quantum”吗?现在你知道了,可以用它来让你的技术朋友们刮目相看。我认为我值得大量赞扬,因为在这篇文章中我没有每句话都提到quantum。

说到时间片术语,Linux内核开发人员使用jiffy时间单位来计算固定频率的定时器滴答声。jiffy 用于测量时间片的长度。Linux 的 jiffy 频率通常是1000 Hz,但可以在编译内核时配置。

对固定时间片调度进行的一点改进是选择一个目标延迟——这是一个进程响应的理想最长时间。目标延迟是指一个进程在被抢占后恢复执行所需的时间,假设进程数量合理。这有点难以想象!别担心,很快会有一个图表来说明。

时间片通过将目标延迟除以任务总数来计算;这比固定时间片调度更好,因为它在进程较少时消除了浪费的任务切换。假设目标延迟为15毫秒,有10个进程,每个进程将获得15/10或1.5毫秒的运行时间。若只有3个进程,每个进程将获得更长的5毫秒时间片,同时仍能达到目标延迟。

进程切换的计算成本很高,因为它需要保存当前程序的全部状态并恢复另一个程序的状态。超过某个临界点,过小的时间片会导致进程切换过于频繁,从而导致性能问题。通常会给时间片设置一个下限(最小粒度)。这意味着当进程数量足够多以至于最小粒度生效时,目标延迟会被超过。

在撰写本文时,Linux 的调度程序使用的目标延迟为 6 毫秒,最小粒度为 0.75 毫秒。

第 2 章 - 时间切片 - 图2

使用这种基本时间片计算的轮转调度接近于大多数现代计算机的做法。不过,这仍然有些简单;大多数操作系统倾向于使用更复杂的调度程序,考虑进程优先级和截止时间。自2007年以来,Linux一直使用一种名为完全公平调度器的调度程序。CFS使用许多高级的计算机科学方法来优先处理任务并分配CPU时间。

每次操作系统抢占一个进程时,它需要加载新程序的已保存执行上下文,包括其内存环境。这是通过告诉 CPU 使用不同的页表来实现的,页表将“虚拟”地址映射到物理地址。这也是防止程序访问彼此内存的系统;我们将在本文的第5和第6章深入探讨这一点。

注意事项 #1:内核抢占性

到目前为止,我们只讨论了用户态进程的抢占和调度。如果内核代码在处理系统调用或执行驱动程序代码时花费太长时间,可能会让程序感觉滞后。

现代内核,包括 Linux,都是抢占式内核。这意味着它们被编程为允许内核代码本身像用户态进程一样被中断和调度。

除非你正在编写内核代码,否则了解这一点并不太重要,但我读过的每篇文章都提到了这一点,所以我也提一下!多了解一些知识通常不会是坏事。

注意事项 #2:历史课

古老的操作系统,包括经典的 Mac OS 和 NT 之前的 Windows 版本,使用的是抢占式多任务处理的前身。不是由操作系统决定何时抢占程序,而是程序本身选择让出 CPU给操作系统。它们会触发一个软件中断,说:“嘿,你现在可以让另一个程序运行了。”这些显式让出是操作系统重新获得控制权并切换到下一个计划进程的唯一方式。

这被称为协作式多任务处理。它有几个主要缺陷:恶意或设计不良的程序可以轻易冻结整个操作系统,并且几乎不可能确保实时/时间敏感任务的时间一致性。出于这些原因,技术界早就转向了抢占式多任务处理,从未回头。