try ai
科普
编辑
分享
反馈
  • 实时调度

实时调度

SciencePedia玻尔百科
核心要点
  • 实时调度优先考虑可预测性和满足截止时间,而非公平性,像最早截止时间优先(EDF)这样的调度器是实现此目的的最优选择。
  • 处理器利用率法则(∑iCiTi≤1\sum_i \frac{C_i}{T_i} \le 1∑i​Ti​Ci​​≤1)为单核系统上独立、周期性任务的可调度性提供了数学保证。
  • 像优先级反转这样的实践挑战必须通过优先级继承等技术来管理,以防止高优先级任务被无限期阻塞。
  • 实时调度的原则不仅在机器人和汽车系统中至关重要,在视频游戏、高频交易和聚变能控制等不同领域也发挥着关键作用。

引言

在大多数计算系统中,速度快就足够了。然而,对于一部分关键技术而言,时机就是一切。从汽车的安全气囊到机器人的手臂,一个操作的正确性不仅取决于其逻辑结果,还取决于它被交付的确切时刻。这就是实时系统的领域,在这些系统中,未能满足截止时间可能是灾难性的。为公平性和平均性能而设计的标准操作系统,不适用于这个充满严格时间约束的世界,这在通用计算和时间关键型应用之间造成了关键的知识鸿沟。

本文通过探索实时调度这门科学来弥合这一鸿沟。首先,在“原理与机制”一节中,我们将剖析使可预测性成为可能的核心理论,从最早截止时间优先(EDF)等截止时间驱动的策略,到允许我们保证性能的优雅的处理器利用率数学。我们还将直面那些威胁这些保证的现实世界中的“小妖精”,如优先级反转。随后,在“应用与跨学科联系”一节中,我们将看到这些原则的实际应用,揭示实时调度在视频游戏、数字音频、拯救生命的医疗设备乃至聚变能科学前沿等惊人广泛的领域中所扮演的无形但至关重要的角色。

原理与机制

想象一下你在玩杂耍。但这不仅仅是为了好玩,而是某个伟大机器的关键部分。如果你掉了一个球,机器就会戛然而止。这就是实时系统的世界。平均速度快是不够的;你必须每次都完美准时。晚一秒钟引爆的安全气囊是无用的。过早一刻抽动的机器人手臂会毁掉它的工件。在这些系统中,计算的正确性不仅取决于逻辑结果,还取决于该结果产生的时间。这是与我们日常使用的台式机和服务器的根本区别。它们的目标是高吞吐量或快速的平均响应。而实时系统的目标是​​可预测性​​。

调度器的困境:紧迫性 vs. 公平性

那么,操作系统是如何处理这些时间关键型任务的呢?一个通用操作系统,比如你笔记本电脑上的那个,通常使用一种“公平”的调度器。例如,轮询(Round-Robin, RR)调度器会给每个任务一小片时间,循环往复,以确保没有单个任务独占处理器。这是民主的,是公平的。但对于实时系统而言,这可能是灾难性的。

考虑一组简单的任务:一个任务每4毫秒需要2毫秒的工作,而其他几个任务需要的工作量较少,但截止时间稍长。一个注重公平的轮询调度器,给每个任务1毫秒的执行时间,可能会先给第一个任务它的初始时间片,然后去服务所有其他任务。当它再次回到第一个任务时,其4毫秒的截止时间已经过去。任务失败了,不是因为处理器太慢,而是因为调度策略优化了错误的目标:公平性而非紧迫性。

这揭示了实时调度的第一大原则:​​紧迫性优先于公平性​​。最有效的策略是那些明确使用截止时间来指导决策的策略。其中最著名的是​​最早截止时间优先(Earliest Deadline First, EDF)​​。它的策略简单得惊人:在任何时刻,总是运行可用任务中截止时间最临近的那个。它是一个抢占式的、“截止时间驱动”的调度器。如果一个新任务到达,其截止时间比当前正在运行的任务更早,它会立即抢占处理器。这种对最紧迫截止时间的无情关注,正是满足所有截止时间所需要的。

利用率法则:时间的预算

这就引出了一个极其重要的问题:我们如何能在任务开始运行之前就知道它们是否都能满足截止时间?我们必须模拟所有可能的交错执行方式吗?那将是不可思议的复杂。奇迹般地,对于像EDF这样的调度器在单处理器上,存在一个既优雅又强大的答案。

我们首先定义任务的​​处理器利用率​​。对于一个周期性任务 τi\tau_iτi​,它每 TiT_iTi​ 毫秒需要 CiC_iCi​ 毫秒的计算时间,其利用率为 Ui=CiTiU_i = \frac{C_i}{T_i}Ui​=Ti​Ci​​。这仅仅是它所要求的处理器时间的比例。对于整个任务集,总利用率为 U=∑iUiU = \sum_i U_iU=∑i​Ui​。

神奇之处在于:对于一组截止时间等于其周期的、独立的、可抢占的周期性任务,EDF能保证每个任务都满足每个截止时间,当且仅当总利用率不超过1。也就是说,∑iCiTi≤1\sum_i \frac{C_i}{T_i} \le 1∑i​Ti​Ci​​≤1。

这是一个功能极其强大的条件。它告诉我们,只要对处理器的总需求不超过其总容量,EDF就是一个完美的调度器。它能在其他调度器失败的地方取得成功。对于轮询调度器无法处理的那组任务,其总利用率是可控的 U=0.95U = 0.95U=0.95。既然 0.95≤10.95 \le 10.95≤1,我们就能以数学上的确定性知道EDF会成功地调度它。这个利用率测试构成了​​接纳控制​​的基础。一个负责任的实时操作系统不会接受一个新任务,除非它能首先验证新的总利用率不会超过1。如果会超过,该任务将被拒绝,从而保护对系统中所有已存在任务的保证。

混合优先级的世界

在现实世界中,并非所有截止时间都是平等的。一个控制生命支持系统的任务具有​​硬截止时间​​——错过它就是灾难性失败。一个为显示解码视频帧的任务具有​​软截止时间​​——错过它可能会导致瞬间的画面卡顿,但并非灾难。一个将日志写入磁盘的任务通常是​​尽力而为​​——我们希望它完成,但没有严格的时间要求。

现代系统必须处理这种混合情况。通用的方法是一个严格的优先级层次结构。硬实时任务被赋予绝对的优先权。软实时和尽力而为的任务只被允许在硬实时任务留下的​​处理器空闲时间​​(processor slack)内运行。多亏了利用率法则,我们可以精确计算出这个空闲时间。如果硬实时任务的总利用率为 URTU_{\text{RT}}URT​,那么处理器时间的 1−URT1 - U_{\text{RT}}1−URT​ 部分可用于其他所有事情。

这个“空闲时间”可以被管理。我们可以用它来运行软截止时间的任务,并试图最小化它们的​​延迟​​(lateness),即它们完成时超出截止时间的程度。或者,为了获得更正式的保证,我们可以将这些空闲时间打包成一个​​服务器​​。例如,一个​​恒定带宽服务器(Constant Bandwidth Server, CBS)​​被赋予每个周期 PPP 内 QQQ 个执行单元的预算。对于主调度器来说,这个服务器看起来就像另一个利用率为 QP\frac{Q}{P}PQ​ 的周期性任务。在服务器内部,一个完全独立的软实时任务世界可以被调度,它们知道自己拥有处理器时间的预留份额。这种美丽的抽象使我们能够构建分层的、可预测的系统。

当理论与现实相遇:机器中的“小妖精”

调度的理论世界是优雅的,但真实的硬件和软件引入了棘手的复杂性——这些机器中的“小妖精”威胁着要破坏我们精心设计的保证。

优先级反转的诡计

最臭名昭著的“小妖精”之一是​​优先级反转​​。想象三个任务:一个高优先级任务(HHH)、一个中优先级任务(MMM)和一个低优先级任务(LLL)。假设 LLL 抢占了一个共享资源(如互斥锁),然后 HHH 变为就绪状态并需要同一个资源。HHH 必须等待 LLL 释放它。这是一个短暂、有界的延迟,尚可管理。但如果当 LLL 持有锁时,中优先级任务 MMM 变为就绪状态呢?由于 MMM 的优先级高于 LLL,它会抢占 LLL。现在,系统中最高优先级的任务 HHH 被卡住,等待着最低优先级的任务 LLL,而 LLL 又被中优先级任务 MMM 阻止运行。高优先级任务实际上被无限期地阻塞了。

这不仅仅是一个理论上的好奇心;它曾导致一次著名的 Mars Rover 任务停滞。解决这个问题的方法和问题本身一样优雅而又诡谲:​​优先级继承​​。当一个高优先级任务在一个由低优先级任务持有的资源上阻塞时,低优先级任务会暂时继承这个高优先级。在我们的例子中,LLL 的优先级将被提升到与 HHH 相同。现在,MMM 再也不能抢占它。LLL 迅速完成其工作,释放资源,并恢复其原始优先级,从而让 HHH 得以继续执行。阻塞时间再次被限制在临界区的短暂时间内。

内核自身的阴影

另一个麻烦的来源可能是操作系统内核本身。为了保护其内部数据结构,内核可能会短暂地禁用抢占。在此期间,任何任务切换都无法发生。如果一个高优先级任务在一个低优先级任务的非抢占式内核段内变为就绪状态,它就必须等待。这个非抢占式段成为另一个阻塞源。对于大多数系统来说,这些段非常小。但在一个标准的、非实时的内核中,它们可能长得不可预测。

这就是为什么真正的硬实时系统通常运行在专门的内核(如带有 PREEMPT_RT 补丁的Linux)上。这些内核经过精心设计,以最小化非抢占部分的长度,将这种阻塞源从可能长达毫秒级减少到仅仅微秒级,这对于截止时间非常短的任务至关重要。

在风暴中幸存:工作负载尖峰和时钟漂移

如果一个任务出乎意料地需要比计划更多的计算时间,可能是由于罕见的数据输入,会发生什么?这种突然的​​工作负载尖峰​​会消耗掉调度中的可用空闲时间。如果系统设计时混合了硬任务和软任务,这可能会迫使调度器做出艰难的选择。为了保证硬截止时间,调度器可能必须​​舍弃​​(drop)软任务的可选部分。通过分析关键时间间隔内的总处理器需求,一个智能的调度器可以计算出为了渡过难关并信守其硬性承诺而必须牺牲的软工作的绝对最小量。

一个更微妙的“小妖精”是​​时钟漂移​​。为计算机时钟计时的晶体振荡器是一种物理设备,它并不完美,且易受温度变化的影响。如果系统的时钟比实时快一点点,比如说快了 (1+ϵ)(1+\epsilon)(1+ϵ) 倍,那么所有由这个快时钟测量的周期和截止时间,在实时中都会缩短。一个名义周期为 TTT 的任务,实际上每 T/(1+ϵ)T/(1+\epsilon)T/(1+ϵ) 秒释放一次。这会抬高其实际利用率。为了在这种情况下保证可调度性,系统的名义利用率必须保持在一个更保守的界限之下:U≤11+ϵU \le \frac{1}{1+\epsilon}U≤1+ϵ1​。这是一个美妙的提醒,我们的计算逻辑模型最终是建立在物理学基础之上的。

超越单核:并行性的希望与风险

到目前为止,我们都只是在一只手上玩杂耍。如果我们有两只手呢?或者四只,几十只?多核处理器引入了真正的​​并行性​​——同时执行多个任务的能力——这与​​并发性​​不同,后者是在单核上通过快速任务切换创造的同时执行的假象。

如果一个任务集在单核上超载(即总利用率 U>1U > 1U>1),它根本无法被调度。但如果有两个核心,它可能变得可行。我们可以对任务进行​​分区​​,将一些任务分配给核心1,其余的分配给核心2,使得每个核心上的利用率都小于1。并行性提供了并发性本身所缺乏的原始容量。

然而,这并非万能药。分区问题是出了名的困难(它等同于装箱问题)。一个天真的分区可能会失败。完全有可能将任务分配给一个核心,使其总利用率小于1,但一个低优先级任务由于受到同一核心上高优先级任务的严重干扰而仍然错过其截止时间。通往正确的多处理器实时调度的道路充满了至今仍是活跃研究领域的微妙之处。

调度即盾牌:驯服时间暴君

最后,我们必须认识到,在一个多用户系统中,实时调度的能力也是一种安全风险。一个获得实时优先级的恶意或编写不佳的程序可以进入一个无限循环。因为它比所有正常的系统任务——包括用户的shell、网络服务,甚至图形界面的部分——优先级更高,它可以永远运行下去,从不让出CPU。这实际上冻结了系统,是一种完美的拒绝服务攻击。

传统的优先级系统对此毫无防御能力。但现代操作系统有一个新工具:​​控制组(cgroups)​​。这个机制允许系统管理员将一个用户的进程放入一个组中,并强制执行硬性的带宽限制。例如,可以配置一个cgroup,允许其实时任务在每个 PPP 微秒的周期内最多获得 RRR 微秒的运行时间。一旦该组中的任务消耗了它们的预算 RRR,无论它们的优先级有多高,它们都会被强制进入休眠状态,直到下一个周期开始。

这为CPU时间创建了一道防火墙。即使一个流氓实时进程试图永远运行,它也会被节流,从而保证至少有 P−RP\frac{P-R}{P}PP−R​ 的CPU时间可用于运行必要的系统服务。这是调度原则的一个美妙而实际的应用,不仅是为了性能,也是为了安全和系统稳定性。从满足截止时间到抵御攻击,实时调度的原则是使我们现代技术世界变得可能、可预测和安全的无形框架。

应用与跨学科联系

在遍历了实时调度的原理之后,我们可能会倾向于将其视为计算机科学中一个专业化,甚至可能有些晦涩的角落。但事实远非如此。实时调度并非一个孤立的学术学科;它是我们现代世界无形的、有节奏的心跳。它是按时编排行动的艺术和科学,这一基本挑战出现在各种各样的情境中,从视频游戏、拯救生命的医疗设备,到在地球上建造一颗恒星的探索。现在让我们来探索这片广阔的领域,看看我们讨论过的优雅原则如何为我们周围的技术注入生命,以及至关重要的——可预测性。

我们所见所闻的世界

我们中许多人首次接触到实时约束的后果,往往是在娱乐世界中,而且常常令人沮丧。想象一下玩一个快节奏的视频游戏。当你按下一个按钮时,你期望屏幕上立即有相应的动作。从你输入到“光子”离开屏幕的这段时间是一个关键的延迟。游戏引擎是复杂的庞然大物,需要同时处理渲染、物理计算、音频处理和人工智能,所有这些都在争夺同一个处理器时间。系统如何决定先做什么?

这正是一个实时调度问题。游戏开发者可以使用像最早截止时间优先(EDF)这样的调度器,而“截止时间”成为表达优先级的强大工具。想象一个超载的场景,处理器不可能为下一帧完成所有工作。通过给渲染任务分配一个非常紧迫的截止时间——比如,8毫秒而不是每秒60帧显示可用的全部16.67毫秒——开发者实际上是在告诉调度器:“无论发生什么,完成这一帧是最高优先级。”这确保了玩家的输入能够迅速得到视觉更新,保持了响应感。当然,代价是其他不太关键的任务,如后台AI更新,可能会错过它们的截止时间,但这是一个为了保护玩家体验而做出的有意识的选择。

类似的原则也支配着数字音频世界。如果你曾使用过数字音频工作站(DAW)来制作音乐,你会期望流畅、不间断的声音流。一个单一的“掉音”或故障就能毁掉一次完美的录音。这个故障是缓冲区下溢——音频硬件没有数据可播放了。防止这种情况需要操作系统精心编排的一场微妙舞蹈。系统必须容纳一系列潜在的延迟:硬件中断可能会稍有延迟(抖动),即使生成音频的应用程序准备好运行,操作系统调度器也可能需要一些时间来分派它。为了防止掉音,系统必须维持一个缓冲区,一个音频数据的“余量”,其大小足以覆盖所有这些最坏情况延迟的总和。一个稳健的设计不仅包括计算这个缓冲区的大小,还包括使用操作系统的实时功能:锁定内存以防止缓慢的页面错误,以及至关重要的是,将为音频硬件提供数据的内核进程的优先级设置得高于生成声音数据的用户应用程序。这确保了交付音频的最后、最紧急的一步永远不会被创建下一批音频这个不那么紧急的任务所延迟。

看不见的生命与安全机器

从娱乐转向工程,风险变得相当高。一个在工厂地板上导航的移动机器人依赖于来自摄像头、激光雷达和惯性传感器的持续数据流来构建一个连贯的世界图景。这种“传感器融合”,通常由扩展卡尔曼滤波器(EKF)等算法执行,是机器人的现实感。虽然这些算法通常运行很快,但它们有时会经历不可预测的执行时间“尖峰”。一个天真的设计将不得不为绝对最坏情况的尖峰来配置处理器,使其在其余时间大部分处于空闲状态。

一种更复杂的、植根于实时理论的方法是创建一个“余量服务器”——每个周期内专门为吸收这些尖峰而保留的CPU时间预算。通过将系统建模为一组周期性任务,工程师可以计算出处理尖峰所需的确切服务器预算,而不会错过任何截止时间,同时确保总CPU利用率保持足够低,以使整个系统都是可调度的。这是一个美丽的例子,说明了正式的调度分析如何使系统既稳健又高效。

当我们将风险提升到关乎人类生命时,这些原则就变得不可商榷。考虑一台医用输液泵,一种必须以精确速率输送药物的设备。其核心控制回路是一个硬实时任务:错过截止时间不是小故障,而是潜在的医疗失败。这种设备的设计是多层次实时分析的大师级课程。在微架构层面,改变处理器频率以节省功耗(动态电压和频率缩放,DVFS)直接影响执行给定数量指令所需的时间。在操作系统层面,调度器必须保证关键的控制回路以绝对最高的优先级运行。

即使是时序信号的来源也至关重要。如果任务的周期性释放是由通用操作系统定时器“滴答”驱动的,那么处理器频率的任何变化或操作系统的延迟都可能引入释放抖动,从而可能导致错过截止时间。一个远为稳健的解决方案是使用一个专用的、独立于主处理器时钟的硬件定时器来触发控制回路。这消除了一个主要的非确定性来源,并且是安全关键系统设计的标志。

技术与商业的前沿

时间的无情前进在最高频交易(HFT)中找到了其最直接的经济表达。在这里,市场数据必须在微秒内处理并提交订单,因为任何延迟都可能意味着盈利与亏损的区别。HFT引擎是一个纯粹的实时系统,其中每个阶段——市场数据评估、策略计算、订单提交——都是一个有确定截止时间的任务。像截止时间单调(DM)这样的调度器被用来根据这些截止时间分配优先级。工程师们计算总处理器利用率,并确保其保持在一个众所周知的理论界限以下,从而提供数学上的保证,即系统即使在活动高峰期也能跟上市场的步伐。

随着时间敏感网络(TSN)的兴起,这种对可预测、低延迟通信的需求现在正从金融领域扩展到工业自动化和汽车网络。想象一个工厂车间,机器人、传感器和控制器必须以微秒级的精度协调它们的行动。TSN提供了这种能力,但前提是整个处理流水线——从网卡的硬件到应用软件——都经过设计以满足严格的延迟预算。分析这样的系统需要对每一微秒的延迟进行细致的核算:数据从网络线缆移动到计算机内存(DMA)的时间,触发中断的时间,操作系统调度等待的应用程序的时间,以及最后,应用程序自身的处理时间。为了满足例如606060微秒的截止时间,工程师可能会选择一个采用专用处理器核心、内核旁路网络以避免操作系统开销以及使用忙轮询代替中断以实现绝对最低延迟的设计。

追求极致性能的架构

实时调度的原则是如此基础,以至于它们不仅适用于应用程序,也适用于它们运行的硬件和软件架构本身。你可能会惊讶地发现,像你计算机内存(DRAM)这样基本的组件,其本质上也是一个实时系统。DRAM中存储数据位的微小电容器会泄漏电荷,必须周期性地刷新。这个刷新命令是一个有执行时间和硬截止时间的任务。如果CPU正在执行一次长时间、密集的内存访问,它可能会阻止内存控制器发出这些至关重要的刷新命令。这些积压的刷新任务必须在它们的截止时间到期前被清除,以防止数据丢失。我们可以使用实时理论来精确地模拟这个场景,计算出内存系统可以容忍的CPU突发访问的最大长度,这是一个美丽的例证,展示了这些概念在硬件和软件中的统一性。

这种统一性在现代虚拟化环境中受到了进一步的考验。当hypervisor(虚拟机监控器)位于虚拟机(VM)和物理硬件之间时,我们如何在一个VM内部运行一个硬实时系统,比如汽车的发动机控制器?一个标准的、尽力而为的hypervisor不提供任何保证。解决方案是一个实时hypervisor,它提供的功能与RTOS(实时操作系统)的功能类似:它允许VM的虚拟CPU被固定到一个物理CPU上,它以严格的优先级调度VM,并且它以有界的、最小的延迟传递虚拟中断。只有在这种确定性的基础上,我们才能使用我们在裸机上使用的相同的响应时间分析,正式证明VM内的所有截止时间都将被满足[@problem.id:3689710]。一个更极端的方法,常用于像汽车制动控制器这样的安全关键系统,是unikernel——一个极简的映像,其中应用程序和必要的OS库被编译成一个单一实体,运行在一个最小的“exokernel”上。这剥离了所有不必要的抽象层,使应用程序能够直接、安全地访问硬件计时器和CPU预留,从而实现极低且可预测的抖动。

然而,我们大多数人并不使用如此奇特的系统。我们使用像Linux这样的通用操作系统。即便如此,实时调度也扮演着至关重要的角色。Linux提供了实时调度类(SCHED_FIFO),其优先级严格高于普通的“公平”任务(CFS)。这带来了一个潜在的危险:一个处于紧密循环中的、行为不当的实时任务可能会独占一个CPU,并饿死所有其他应用程序,使系统无响应。为了防止这种情况,Linux为实时带宽控制提供了一个强大的cgroup功能。这允许管理员为实时任务套上一个“缰绳”,在一个给定的周期(例如,101010毫秒)内授予它们一个最大运行时间(例如,444毫秒)。这确保了无论实时任务运行得多么激进,它们在消耗完预算后都会被节流,从而保证CPU总是有空余时间用于其他必要的系统功能。

科学的前沿

实时控制最令人敬畏的应用或许在于人类科学探索的最前沿:控制托卡马克(tokamak)装置中的聚变等离子体。托卡马克使用强大的磁场来约束比太阳核心还热的等离子体。这种等离子体的某些构型天生就不稳定,就像试图把铅笔立在笔尖上一样。例如,等离子体的垂直位置会以指数增长率开始漂移。如果不加以控制,这种不稳定性会在几毫秒内导致等离子体撞击反应堆壁,从而熄灭反应并可能损坏机器。

维持等离子体的唯一方法是通过高速反馈回路。一个实时系统必须不断地测量等离子体的位置,计算对磁场必要的校正,并命令强大的放大器来施加它。这整个周期——从测量到驱动——必须在毫秒的一小部分时间内完成。这个控制回路中的任务,从状态估计器到控制器算法,都是最硬的硬实时任务。错过截止时间是不可接受的。时间约束直接来源于等离子体本身的物理特性;总的端到端延迟必须小于不稳定性增长某个因子(例如,翻倍)所需的时间。利用实时调度的原理,物理学家和工程师可以计算出所有控制和诊断任务的总处理器利用率,并使用像EDF这样的调度器来保证这个宏伟的科学仪器可以被驯服。

从我们屏幕上的像素到人造太阳的核心,实时调度为我们掌握时间提供了框架。它是一种统一的语言,使我们能够在一个不可预测的世界中构建可预测、可靠和安全的系统。它是推动我们技术文明前进的安静而有纪律的引擎。