
在任何复杂的计算机系统中,从单个多核处理器到庞大的云数据中心,其核心挑战都是如何公平、高效地管理共享资源。一个系统如何能保证每个任务获得其应有的份额,同时又能为紧急工作负载提供最大性能?基于信用的调度提供了一种优雅而强大的解决方案,它将抽象的公平性问题转化为具体的记账系统。该模型不依赖于简单的轮流机制,而是为资源访问引入了一种“货币”,解决了在保障所有用户公平份额与允许必要时高性能爆发之间的内在冲突。
本文将深入探讨这一基本概念。首先,在 原理与机制 部分,我们将探讨赚取、花费和节省资源信用的核心“银行业务”类比,以理解它如何确保公正性和响应能力。随后,在 应用与跨学科联系 部分,我们将看到同样的想法如何远远超出了 CPU 的范畴,为管理从磁盘 I/O 和内存到硬件安全的一切事务提供了一个统一的框架。
想象一下,你和你的朋友们共享一个速度非常快的厨房。每个人都想做饭,但只有一个炉灶。你如何决定谁可以使用它,以及使用多长时间?如果一个人独占了它,其他人就会饿肚子。如果你们只是简单轮流,那么那个只需要用微波炉热个小零食的朋友,和那个准备三道菜大餐的朋友又该怎么办?你如何确保公平而又不造成浪费?
这是资源调度的经典困境,操作系统每时每刻都在 CPU 上面临这个问题。基于信用的调度是为解决此问题而设计的最优雅、最强大的解决方案之一。它与金钱无关;它是一个关于公平的记账系统,一个将复杂的仲裁问题简化为简单算术的优美机制。
从本质上讲,基于信用的调度器将 CPU 时间视为一种货币。每个想要使用 CPU 的进程或虚拟机都会被分配一个银行账户。
赚取(补充率):系统每时每刻都会向你的账户中存入一股稳定的“CPU信用流”。这就是 补充率。这个速率对每个人来说并不相同;它根据你在系统中的 公平份额 来设定。如果你有权获得 10% 的 CPU,你的账户将以相当于持续使用 10% CPU 的速率进行补充。
花费(消耗):当你在 CPU 上运行时,你会从你的账户中花费信用。如果你使用一个完整的 CPU 核心一秒钟,你可能会花费一个信用。
节省(信用桶):如果你现在不需要 CPU 怎么办?也许你在等待用户输入或文件下载。在这段空闲时间内,你不花费信用。相反,你的补充率带来的存入开始在你的账户中积累,就像存钱一样。当然,你不能永远存下去;你能持有的信用数量是有限的,这个限制被称为 信用上限 或 桶大小。
这个简单的赚取、花费和节省模型是其基础。它将“公平”这一抽象概念转化为一个有形的量:你的信用余额。调度器的规则因此变得异常简单:在任何给定时刻,拥有最多信用的进程获得运行机会。
这个银行业务系统同时实现了两个看似矛盾的目标:它保证了长期的公平性,同时允许短期的超高性能。让我们通过一个云提供商为不同客户管理虚拟机(VM)的例子来看看这是如何实现的。
首先是 长期公平性。在很长一段时间内,你花费的信用不能超过你赚取的信用。这意味着你的平均 CPU 使用率永远不会超过你的补充率。如果一个云提供商想保证客户获得总 CPU 容量 中 的公平份额,他们只需将客户的补充率 设置为该份额:
这个单一的方程优雅地强制执行了长期合同。无论客户的使用模式是突发性的还是平稳的,其平均消耗都与这个速率挂钩。
但性能又如何呢?这就是节省信用的魔力所在。如果一个客户的虚拟机一直处于空闲状态(例如,一个没有访客的 Web 服务器),它就会一直积累信用,直到达到其上限 。突然,大量的流量涌入。该虚拟机需要一次巨大的、短期的 CPU 算力爆发。由于它有大量的信用余额,调度器允许它以远高于其平均份额的速率运行,以最大可能速率消耗信用。这就是 突发性能。
这次爆发能持续多久?假设该虚拟机在爆发期间使用了所有可用的 CPU 核心 。它以速率 花费信用,但仍以速率 赚取信用。因此,它的净花费速率是 。这次爆发将持续到其节省的信用 耗尽为止。因此,爆发的持续时间 为:
这为系统设计者提供了一个直接控制突发性的杠杆。如果他们希望允许一个虚拟机以全功率爆发最多(比如说)10秒,他们可以简单地重新排列公式并相应地设置信用上限:。通过调整两个参数——用于公平性的补充率 和用于突发性的桶大小 ——设计者可以创建一个复杂且可预测的服务。
信用系统的美妙之处在我们观察它解决冲突时最为明显。想象一个虚拟机主机上的经典“吵闹邻居”场景。我们有一个大型、消耗 CPU 的虚拟机,称之为“Hercules”,它开始时拥有满满一桶信用。我们还有一个小型的、轻量级的虚拟机,称之为“Swift”,它大部分时间处于空闲状态,但需要在请求到来时立即响应。Swift 开始时信用为零。
在时间零点,Hercules 拥有更多信用,所以它开始运行,消耗整个 CPU。它的信用余额开始下降。与此同时,Swift 处于空闲状态,但正通过其补充率悄悄地积累信用。一场竞赛已经开始。
Swift 有机会运行吗?是的,信用系统保证了这一点。两件事正在并行发生:Hercules 的余额在消耗,而 Swift 的余额在增长。最终,Swift 的信用水平 必然 会超过 Hercules。问题是,在什么时候?
有趣的是,这可以通过两种方式发生。如果 Swift 的补充率非常高,它积累信用的速度可能会非常快,以至于在一次直接的“拦截”中超过 Hercules 不断下降的余额。这所需的时间取决于积累和花费的 相对速率。
然而,如果 Swift 的补充率较低,则会发生不同的情况。它可能会达到自己的信用上限,然后就带着满桶的信用耐心等待。Hercules 继续消耗自己的信用,直到其余额最终低于 Swift 的上限值。在那一刻,Swift 获得了 CPU。
这里的关键洞见是,存在一个 Swift 的 临界补充率。低于这个速率,运行所需的时间由“拦截”竞赛决定。高于这个速率,运行所需的时间变为一个常数,仅取决于 Hercules 消耗其初始优势所需的时间。通过将 Swift 的补充率设置在该临界阈值或之上,系统管理员可以保证一个最小的、最坏情况下的响应时间,从而有效地保护它免受吵闹邻居的影响。信用系统不仅提供了公平性,还提供了一种可调节的响应能力保证。
那么,“信用”到底是什么?它仅仅是一种巧妙的记账技巧吗?答案更为深刻。信用是一种 记忆。它是系统记录哪些进程被不公平地阻止运行的方式。
在操作系统的早期,设计者面临着 饥饿 问题,即如果总有更高优先级的进程准备就绪,一个低优先级的进程可能永远无法运行。一个经典的解决方案是 老化:当一个进程在就绪队列中等待时,它的优先级会慢慢增加。等待的时间越长,它的优先级就越高,直到最终成为最高优先级的进程并得以运行。
让我们仔细看看。一个在等待时积累信用的进程,和一个在等待时优先级“老化”的进程,看起来像是不同的想法。但它们是吗?
想象一个老化系统,其中一个等待进程 的年龄 以速率 增加。现在考虑一个信用系统,其中它的信用 以速率 增加。在这两种情况下,调度器都会选择具有最高值的进程。事实证明,如果速率成正比:(其中 是某个常数),那么这两个系统在数学上是等价的。如果这个条件成立,年龄最高的进程将永远是信用最多的那个进程。排名是相同的。调度决策是相同的。
这是一个美妙的统一。看似经济学概念的基于信用的“漏桶”模型,只是老化这一基本公正原则的一个具体、可量化的实现。它是一种确保耐心最终得到回报的机制。
信用的力量甚至不止于此。它是一个通用的工具,用于追踪与理想公平份额的偏差。
考虑一个任务,它不仅是 CPU 密集型的,而且在需要 CPU 和等待 I/O(如从磁盘读取)之间交替。当它等待磁盘时,它没有使用其应有的 CPU 公平份额。那些未使用的份额应该怎么办?如果它被简单地分配给其他任务,那么我们的 I/O 密集型任务从长远来看,将获得比同等权重的 CPU 密集型任务更少的总 CPU 时间。这将违反长期公平性。
优雅的解决方案再次是信用。当任务因 I/O 而阻塞时,系统会追踪它 本应 获得的 CPU 份额,并将其累积为信用。当任务完成 I/O 并准备再次运行时,它会带着大量的信用余额返回。调度器随后可以利用这个余额暂时提升任务的 有效权重。这使得它能获得比正常情况下更大的 CPU 时间片,使其能够“追赶”上它错失的时间。当它消耗掉这些额外的服务时,它的信用余额会减少,其有效权重也恢复正常。
这个机制至关重要。它表明信用不仅仅用于管理那些想要爆发的贪婪用户。它们也用于保护行为良好但间歇性的任务,确保自愿为 I/O 放弃 CPU 不会导致它们从长远来看失去其公平份额。
然而,值得注意的是,信用机制的 实现 很重要。一个简单的调度器,仅仅在时间片开始时给予信用,然后平等对待所有信用持有者,可能无法提供细粒度的公平性。真正的比例调度器通常更深入地集成信用概念,用它来持续调整任务的优先级或虚拟运行时间,确保公平性不仅在时间周期内维持,而且在每时每刻都得以维持。
从一个简单的银行业务类比,我们得出了一个强大而通用的机制。基于信用的调度提供了一种谈论公平性的语言,一套控制性能的杠杆,以及一种记忆,以确保在一个计算机系统复杂、混乱的生命周期中,正义最终得到伸张。
现在我们已经探索了基于信用的调度的优美机制,一件奇妙的事情发生了。你开始在各处看到它的身影。它并非某个为解决操作系统中单一问题而设计的孤立、巧妙的技巧。相反,它是为任何具有共享资源的系统协调公平与控制的一种基本设计模式。它的逻辑如同经济学中的货币概念一样普遍而强大。其核心洞见非常简单:要实现真正的公平,你不能只计算轮次;你必须核算每一项行动的真实 成本。这种从“下一个是谁?”到“它要花多少钱?”的视角转变,是为各种出人意料的问题解锁优雅解决方案的关键,从旋转的硬盘盘片到现代多核处理器的安全。
让我们从一个非常具体的问题开始:两个程序正在争夺对一个老式磁性硬盘的访问权。一个程序,我们称之为“图书管理员”,正在从头到尾顺序读取一个大文件。另一个程序,“考古学家”,正在挖掘分散的数据碎片,向磁盘各处发出随机读取请求。一个天真的调度器可能会决定通过让他们轮流进行,“公平地”每个程序执行一次读取请求。但这真的公平吗?
对于“图书管理员”来说,读取顺序文件的下一个数据块成本很低。磁盘的读/写磁头已经在正确的位置;它只需等待盘片旋转一小段距离,然后就可以流式传输数据。对于“考古学家”来说,一次随机读取的成本极其高昂。磁头必须物理地移动穿过磁盘(一次“寻道”),然后等待盘片的正确部分旋转到其下方(“旋转延迟”)。这些开销可能比实际数据传输花费的时间长数千倍。给每个程序一次“轮次”意味着“考古学家”为极少量的数据消耗了大量的宝贵磁盘时间,而“图书管理员”则以极少的磁盘时间获得了大量数据。
真正公平的解决方案是给每个程序相等的 时间预算。这正是基于信用的调度器可以做到的。系统的“货币”变成了磁盘时间的毫秒数。两个程序都以相同的速率赚取这些时间信用。“图书管理员”可以用非常低的价格“购买”大量连续数据块的爆发式访问,保持了顺序访问的巨大效率。与此同时,“考古学家”则用它的预算来“购买”其单个、昂贵的随机数据块。两者花费信用的速度可能不同,但从长远来看,两者都被分配了对基本资源——磁盘的繁忙时间——的同等份额。这个优雅的机制同时实现了公平性和高性能,这是我们旅程中的一个共同主题。
操作系统是共享资源的终极管理者,它以极其复杂的方式运用着基于信用的记账原则。
考虑操作系统内部的一个常见场景:一个“生产者”进程生成数据并通过通信管道或缓冲区发送给一个“消费者”进程。如果生产者快而消费者慢,会发生什么?缓冲区将会被填满。一个不负责任的操作系统可能会简单地丢弃多余的数据,导致数据损坏和混乱。一个设计良好的操作系统则使用流控制。
最优雅的流控制形式是反压,这是基于信用思想的自然应用。当缓冲区满时,可以说生产者用完了“空间信用”。操作系统不会丢弃它的数据;它只是让生产者进入睡眠状态。生产者进程被阻塞,不消耗 CPU,直到消费者从缓冲区读取一些数据。读取的行为释放了空间,操作系统可以将其视为向生产者返还新的“空间信用”。在收到这些信用后,生产者被唤醒并被允许再次写入。这个简单、自动且高效的机制确保了没有数据丢失,并且系统保持稳定,生产者的速率优雅地匹配消费者的处理能力。
信用的力量远远超出了简单的速率限制。现代系统通常有多个相互竞争的目标。想象一组应用程序在一台服务器上运行,每个都有不同的重要性或“权重”。我们希望根据它们的权重给予它们公平的 I/O 带宽份额,但我们 也 希望尽可能有效地利用系统的共享内存缓存以最大化总性能。
对缓存进行静态、僵化的分区很简单,但通常很浪费。一些应用程序从一点额外的缓存中获益巨大,而另一些则可能不然。在这里,操作系统可以扮演一个动态的、基于信用的市场的管理者。每个应用程序组都获得稳定的缓存信用“收入”,这定义了它们的长期公平份额。然而,操作系统持续监控每个应用程序从额外一页缓存中能获得多少性能“性价比”(即缓存命中率的增加)。一个有望获得巨大收益的应用程序可以暂时从一个不那么需要缓存的应用程序那里“购买”或“借用”缓存空间。这种借用由信用系统调节,确保从长远来看,每个人的使用量平均下来都符合其应有的份额。这种复杂的博弈允许系统在短期内追求最优的全局性能,同时在长期内保证加权公平,这是效用与公正的美妙结合。
让我们将这个想法扩展到广阔的云计算世界。当你在云中启动一个虚拟机(VM)时,你是在与无数其他租户共享庞大的服务器。云提供商面临一个复杂的调度问题:它应该将你的突发性应用放置在单个强大的处理器核心上,还是将其分散在几个较弱的核心上?
在这里,信用成为经济模型的明确组成部分。提供商可能会根据你的虚拟机消耗的 CPU-秒来向你收费——这些是你消耗的信用。但还有另一个隐藏的成本:“窃取时间”(steal time)。这是你的虚拟机准备好运行但却因为物理 CPU 正在为另一个租户服务而被卡住等待的墙上时钟时间。对你来说,这是生产力的损失。对提供商来说,这是他们承诺的服务质量(Quality of Service)的下降。因此,提供商的调度器可以定义一个总成本函数,权衡你消耗的 CPU 信用的成本与你遭受的窃取时间的惩罚成本。通过使用排队论的数学模型来模拟对核心的竞争,云的虚拟机管理程序(hypervisor)可以预测这些相互竞争的成本,并做出智能的放置决策,优化其资源分配以满足其服务水平协议(SLAs)。
基于信用的资源管理原则是如此基本,以至于它不仅仅局限于软件。它经常被直接刻蚀到硬件本身。
在一个复杂的片上系统(SoC)内部,许多不同的组件——网卡、存储控制器、图形处理器——都在争夺对主内存总线的访问权。这是我们磁盘调度问题的硬件级版本。一个简单的固定优先级方案是危险的;一个高优先级的设备可能会饿死所有其他设备。为了防止这种情况,硬件设计者直接在芯片中实现了一种“令牌桶”算法。每个设备通道都有一个小的硬件计数器,以设计者指定的速率累积“字节信用”。一个设备只有在其计数器持有足够信用以“支付”传输费用时,才被允许发起内存传输。这个硬件交通警察确保没有单个设备可以独占内存总线,并且每个设备都获得其指定的带宽份额,从而保证了全系统的稳定性。
我们已经看到信用被用于实现公平、性能和稳定性。但也许它们最深刻的应用是在安全领域。在现代多核处理器中,运行在不同核心上的进程通过片上网络进行通信。假设一个绝密的密码学进程在核心1上运行,而一个可能不受信任的网络浏览器在核心2上运行。如果它们的网络流量共享相同的物理线路,浏览器或许能够通过测量自己网络数据包的微小延迟来推断密码学进程在做什么。一次加密操作可能会导致一种特定的拥塞模式,恶意进程可以检测到。这是一种“时序侧信道攻击”。
我们如何建立一堵墙来抵御这种无形的信息泄露?解决方案在于绝对的资源隔离,并在硬件中强制执行。我们可以为高安全域和低安全域创建独立的“虚拟通道”,为它们提供不同的缓冲资源。但这还不够。我们还必须在物理链路上划分 时间。使用像时分复用(TDM)这样严格的、非工作保持(non-work-conserving)的调度策略,高安全域被分配了其私有的、保留的传输时间槽。这些时间槽 只 为其使用而保留。即使高安全域没有数据要发送,它的时间槽也不会被让给低安全域;它们只是被闲置。这是基于信用的分配的终极形式:一种预付费、不可退款、有保障的预留。其结果是,高安全域的通信时序变得完全独立于系统中任何其他活动,从而封锁了时序信道,将一个共享资源变成了一条私密、安全的路径。
从简陋的硬盘到云计算的经济学,再到硬件安全的基础,用公平的货币来核算资源使用的简单而优雅的思想,提供了一个统一的框架。它使我们能够构建不仅高效、公平,而且稳定、安全的复杂系统。