try ai
科普
编辑
分享
反馈
  • 多核中断

多核中断

SciencePedia玻尔百科
核心要点
  • 向多核处理器的过渡使得简单的禁用中断方法失效,因而需要新的全局同步机制,如硬件原子指令和处理器间中断 (IPI)。
  • 现代的消息信号中断 (MSI-X) 能够实现精确的中断导向,这对于优化高速网络和存储 (NVMe) 至关重要,因为它能将数据处理与负责的核心对齐。
  • 内部系统一致性任务,如 TLB 刷写 (shootdown),依赖于广播 IPI,这可能造成全局性能瓶颈,将内存管理直接与应用程序的响应性联系起来。
  • 有效的中断管理涉及在吞吐量和响应性之间进行复杂的权衡,这由操作系统特性和硬件技术共同管理,并对系统功耗和散热产生直接影响。

引言

在现代计算架构中,多核中断如同中枢神经系统,使处理器能够对来自硬件设备和其他处理器核心的持续事件流做出反应。然而,从单核到多核处理器的转变,打破了过去简单而优雅的同步模型,在通信和性能方面引入了深刻的挑战。本文旨在弥合“知道中断存在”与“理解如何管理中断以协调多核系统这首复杂交响乐”之间的差距。在接下来的章节中,您将学习现代中断处理的基本原理,并了解它们如何应用于解决现实世界中的性能问题。我们将首先探讨“原理与机制”,从单处理器系统的“失落乐园”出发,深入了解定义当今多核格局的 IPI 和 MSI-X 的复杂内部构造。随后,在“应用与跨学科联系”中,我们将看到这些机制如何被用来实现高速数据包处理、释放现代存储的潜力,以及平衡吞吐量、延迟乃至热力学之间微妙的相互作用。

原理与机制

要理解多核中断的世界,我们必须首先回到一个更简单的时代,一个孤独的时代。想象一台只有一个处理器核心的计算机。在这个孤独的世界里,管理中断是一件优雅甚至近乎琐碎的事情。但正如我们将看到的,第二个、第三个乃至第 N 个核心的出现打破了这个乐园,迫使我们去发现全新的通信和控制原理。

失落的乐园:从单处理器到多核

在单核计算机中,处理器就像一个安静图书馆里勤奋的图书管理员。它逐一处理任务列表。​​中断​​就像前台的铃声响起——一个来自键盘、网卡或内部计时器等设备的信号,要求图书管理员立即关注。为了处理这个请求,图书管理员会在当前任务中放一个书签,走到前台(即​​中断服务程序​​ (ISR)),处理请求,然后回到书中刚才离开的地方继续工作。

那么,如果图书管理员正在进行一项精细的操作——比如更新必须保持一致性的卡片目录——该怎么办?这是一个​​临界区​​。此时发生中断可能会是灾难性的。解决方法非常简单:图书管理员可以在门上挂一个“请勿打扰”的牌子。在处理器术语中,这就是​​禁用中断​​。当中断被禁用时,铃声无法响起,没有人能进入图书馆,图书管理员可以保证在不被抢占的情况下完成其关键任务。这一个强大的指令创造了一个完美的、不可分割的工作块。

但这里有一个陷阱,一丝未来复杂性的暗示。如果按铃的人——中断处理程序本身——也需要使用同一个卡片目录怎么办?如果我们的图书管理员禁用了中断,拿到了目录的锁,然后一个中断发生(如果中断被禁用,这是不可能的,但让我们考虑一个稍有不同的情况),或者更现实地说,如果图书管理员在没有禁用中断的情况下拿到了锁,就可能发生致命的拥抱。一个中断到来,图书管理员在持有目录钥匙时被抢占,而中断处理程序也试图获取这把钥匙。处理程序将永远等待一把由它自己暂停的人持有的钥匙。这就是​​死锁​​。唯一的出路是建立一条严格的规则:在单核上,你必须总是在尝试获取中断处理程序也可能需要的锁之前禁用中断。通过将像 ​​Test-and-Set​​ 这样的原子指令与中断屏蔽相结合,我们在不同抽象层次上构建原子性,以创建一个真正受保护的区域。

然而,这个单处理器乐园建立在一个脆弱的假设之上:图书馆里只有一个图书管理员。

当我们转向​​多核处理器​​时,我们不再只有一个图书管理员;我们有了一整个团队,每个成员都在自己的办公桌上独立工作,但共享同一个中央卡片目录。现在,如果核心 0 的图书管理员挂起他们的“请勿打扰”牌子(禁用本地中断),这对核心 1 的图书管理员完全没有影响。核心 1 会继续工作,完全不知道核心 0 要求安静的请求。

这打破了我们简单的同步模型。想象一下,核心 0 和核心 1 都需要更新同一个共享数据。两者都执行 disable_interrupts()。然后两者都检查共享资源,发现它空闲,并进入临界区。结果是一片混乱。两个核心同时修改相同的数据,导致状态损坏。旧的技巧毫无用处,因为它提供的是局部原子性,而我们需要的是全局互斥。在一个核心上禁用中断就像在飓风中低语。为了协调多个核心,我们需要一个所有核心都能看到并遵守的机制——一个在整个芯片上都有效的真正的“锁”,通常由硬件​​原子读-改-写​​指令构建。

中断的管道:从共享电话线到短信

那么,一个中断信号究竟是如何找到通往核心的路径的呢?这种管道的演变是一个从暴力手段到外科手术般精确的过程。

传统的方法,称为​​基于线的中断 (INTx)​​,就像一条共享的电话线。主板上铺设了少数几根物理线路,多个设备可能连接到同一条线上。当一个设备需要注意时,它基本上会在线路上大喊。一个中央交换台,即 ​​I/O 高级可编程中断控制器 (IOAPIC)​​,会听到喊声,检查其目录以查看哪些设备在该线路上,然后将呼叫转发给一个处理器核心。这种方式很笨拙。如果多个设备共享一条线路,很难分辨是谁在喊叫,而且路由不灵活。

现代的革命是​​消息信号中断 (MSI)​​ 及其更强大的兄弟 ​​MSI-X​​。设备不再使用共享线路,而是将中断作为消息发送。它对处理器​​本地 APIC (LAPIC)​​ 指定的一个地址执行一次特殊的内存写入。这就好比在拥挤的大厅里大喊大叫和直接给某人发短信的区别。消息本身包含“中断向量”,这是一个告诉 CPU 发生了哪种类型事件的数字。

这个新模型之所以是颠覆性的,原因有二:

  1. ​​消除共享资源:​​ 没有了共享的物理线路,消除了一个主要的性能瓶颈,并简化了系统设计。

  2. ​​中断亲和性:​​ 这是其杀手级特性。因为 MSI 是一个定向消息,操作系统可以极其精确地对设备进行编程。考虑一个拥有数十个数据队列的高性能网卡。有了提供多达 2048 个唯一向量的 MSI-X,操作系统可以说:“对于队列 0 上的流量,向核心 0 发送向量 100。对于队列 1 上的流量,向核心 1 发送向量 101”,依此类推。这将每个数据流的处理工作直接引导到一个专用的核心,极大地减少了争用并最大化了吞吐量。一个拥有 18 个接收队列和 18 个发送队列的现代网卡,可能需要 38 个唯一的中断向量才能在这种高效的“分离向量模式”下运行,这对于 INTx 来说是不可能的任务,但对于 MSI-X 来说却轻而易举。

核心间的对话:处理器间中断

既然设备可以向核心发送定向消息,那么下一步合乎逻辑的就是让核心之间互相发送消息。这种机制,即​​处理器间中断 (IPI)​​,是多核系统中所有有意义的协调的基础。IPI 本质上是一个核心到核心的 MSI。核心 0 可以向中断控制器中的一个特殊寄存器写入数据,指定一个目标(例如,“核心 5”)和一个中断向量。然后,硬件将这个消息传递给核心 5 的 LAPIC。

当 IPI 到达时,如果目标核心的中断是启用的,它会陷入(trap)。它会在两条指令之间精确地停止执行,将下一条待执行指令的地址保存在一个特殊寄存器中(如​​异常程序计数器​​或 EPC),然后跳转到由 IPI 向量决定的特定处理程序例程。这允许一个核心命令另一个核心执行一个动作,例如清除缓存或运行新任务。

协调的交响曲:TLB 刷写

没有任何例子能比 ​​TLB 刷写 (Shootdown)​​ 更好地说明这些机制的力量和必要性。每个现代 CPU 都使用虚拟内存,将程序看到的地址(虚拟地址)转换为物理 RAM 中的地址。为了加速这一过程,每个核心都有一个用于这些转换的私有缓存,称为​​转换后备缓冲区 (TLB)​​。

问题来了:当操作系统需要更改一个映射时——例如,出于安全原因撤销程序对某个内存页的访问权限——会发生什么?操作系统更新了内存中的中央页表,但是核心 1、核心 2 和核心 3 可能都在其私有 TLB 中缓存了旧的、过时的转换。如果它们继续使用它,它们可能会访问不再应该访问的内存,这是一个巨大的安全和稳定性故障。

系统必须强制所有核心丢弃它们过时的 TLB 条目。这就是“刷写”,一场精心协调的芭蕾舞:

  1. ​​更新:​​ 发起核心(比如核心 0)获取一个锁并更新共享内存中的页表条目。

  2. ​​广播:​​ 核心 0 向所有可能正在使用该映射的其他核心发送一个 ​​IPI​​。消息很简单:“使虚拟地址 X 的 TLB 条目无效。”

  3. ​​失效与确认:​​ 每个目标核心接收到 IPI,立即中断它正在做的事情,运行一个处理程序来从其本地 TLB 中刷新特定的条目,并向核心 0 发回一个确认。至关重要的是,目标核心必须使用特殊的内存屏障指令来确保失效操作在任何后续指令可以使用过时转换之前完成。

  4. ​​同步:​​ 核心 0 必须等到它从所有目标核心收到确认为止。只有到那时,它才能确定系统中不再存在任何过时的转换,并且可以安全地(例如)将释放的物理内存页重新用于其他目的。

这个过程突显了多核系统中深度的相互依赖性。想象一个场景,核心 2 为了执行一个快速的关键任务而短暂地禁用了它的中断。当它的中断关闭时,它对来自核心 0 的刷写 IPI 是“听不见”的。整个系统——所有 N 个核心——现在都必须等待。如果核心 2 的中断禁用区持续了 80 μs80\,\mu\text{s}80μs,那么释放单个内存页的全局操作就被延迟了至少 80 μs80\,\mu\text{s}80μs。一个核心上的局部决策变成了整个机器的全局性能瓶颈。

这整个精巧的舞蹈都建立在信任之上。如果一个配置错误或恶意的设备可以伪造自己的 IPI 或 MSI 消息怎么办?它可能触发其他设备的处理程序,导致拒绝服务攻击,甚至试图冒充操作系统。为了防止这种情况,现代系统包含一个称为 ​​IOMMU​​ 的硬件防火墙,它实现了​​中断重映射​​。它检查每一条中断消息,使用设备的唯一硬件 ID 来验证它是否只向其被操作系统授权的目标和向量发送中断,并丢弃任何非法消息。这确保了只有合法的参与者才能参与系统的中断驱动对话,从而保障了多核通信的根基。

应用与跨学科联系

指挥棒:编排硅基交响乐

在我们之前的讨论中,我们揭示了多核中断的复杂机制——信号、信使和路径。我们看到,一个拥有众多核心的现代处理器如何依赖这些信号来对世界做出反应。但是,拥有这些机制是一回事;有效地使用它们则是另一回事。一个交响乐团拥有所有需要的乐器,但如果没有指挥家来引导节奏、提示声部、平衡动态,结果将不是音乐,而是噪音。

多核处理器也是如此。中断的原理提供了乐器,但其应用的艺术和科学则谱写出音乐——我们期望从现代计算机获得的惊人性能、无缝响应和安静高效。正是在这里,处理器间中断(IPI)和中断亲和性的抽象机制,变成了服务器在处理数百万请求时毫不动摇、或笔记本电脑在高负载下保持凉爽运行的具体现实。在本章中,我们将踏上一段探索这门艺术的旅程,看一看对中断的精心引导如何为硅基管弦乐队带来和谐,将比特和字节的数字世界与时间、能量甚至热量的物理约束联系起来。

高速数据包处理的艺术

在高速网络世界中,中断管理的挑战无处能比。想象一下,现代网络接口就像一根消防水带,每秒向系统喷射数百万个数据包。在单核世界里,任务虽然艰巨但很简单:一个核心必须处理所有事情。在多核世界里,我们有很多只手来接住这个数据流,但这引发了一个新问题:我们如何分配工作而让核心之间不至于相互绊倒?

一种天真的方法可能是让传入数据包的中断落在任何可用的核心上。这种“随机分发”的方法会导致混乱。一个数据包的数据可能到达一个核心的内存,它的中断可能由第二个核心处理,而需要处理它的应用程序线程可能正在第三个核心上休眠。结果是为了协调一个数据包的旅程,就引发了一连串昂贵的跨核通信、缓存未命中和 IPI。

我们指挥家工具箱中的第一个工具是​​中断亲和性​​——即将来自特定设备的中断“绑定”到特定核心的能力。但是绑定到哪个核心呢?一项引人入胜的分析揭示,最直观的答案往往是错误的。人们可能认为最好在同一个核心上处理数据包的中断及其应用处理,以最大化数据局部性。然而,这可能导致这两个任务争夺核心有限的资源,特别是其缓存,导致“缓存污染”,即一个任务反复驱逐另一个任务需要的数据。另一个想法是将工作分得很开,放在不同的处理器插槽上,以确保没有干扰。但这会直接撞上非统一内存访问(NUMA)的墙壁,访问远程插槽上的内存速度会慢得多。

最优策略通常是一种微妙的平衡。对于许多高 I/O 工作负载,最佳点是将与某个网络队列相关的所有工作都保留在同一个插槽上,以受益于快速、共享的末级缓存,但是将中断处理绑定到一个专用的核心,而将应用程序处理绑定到该同一插槽上的另一个核心。这种“同插槽、不同核”的方法既避免了跨插槽数据流量的沉重代价,也避免了共享单个核心所带来的缓存破坏。它需要一个廉价的插槽内 IPI 来交接工作,但这个小小的成本被效率和可预测性的提升所弥补,绰绰有余。

现代网卡和操作系统提供了更精细的工具:​​接收端缩放 (RSS)​​。RSS 允许硬件检查传入的数据包,并根据其头部信息(例如,源/目的 IP 地址和端口),将不同的网络“流”引导到不同的硬件队列,每个队列的中断都可以被绑定到不同的核心。这实现了一种优美的对齐:我们可以将一个流的中断映射到其相应应用程序线程正在运行的那个核心。挑战于是变成了一个复杂的资源分配难题。给定一组具有不同数据率的流和具有有限处理能力的核心,系统必须设计一个映射,使每个核心都保持在其能力范围内,同时最小化会产生跨核开销的“错误映射”流的数量。解决这个难题对于最小化 IPI 成本以及当两个核心访问相同数据结构时发生的缓存行弹跳至关重要。

但如果硬件没有那么先进呢?并行化的原理是如此强大,以至于我们可以在软件中模拟这种导向。在非对称多处理 (AMP) 模型中,我们可以指定一个“主”核心来接收所有硬件中断。这个主核心只做最少的工作:它检查每个数据包,并像一个邮件分拣员一样,将其放入相应“工作”核心的软件队列中。然后它触发一个软件中断来唤醒工作核心。这种设计将一个串行化的硬件瓶颈转变为一个并行的软件流水线,极大地提高了系统的总吞吐量。

超越网络:I/O 革命

多核中断管理的革命远不止于网络领域。思考一下存储设备的演变。几十年来,像 SATA(使用 AHCI 协议)这样的接口都是基于单核时代构思的模型构建的。它们只有一个命令提交队列和一个用于完成信号的中断向量。在多核系统上,这个单一队列成为一个严重的瓶颈。多个想要发出 I/O 请求的核心都必须争夺一个锁来访问该队列,导致串行化和大量的缓存一致性流量,因为队列的数据结构在核心之间来回弹跳。更糟糕的是,所有完成中断都落在一个指定的单个核心上,破坏了由不同核心提交的任何请求的 CPU 亲和性。

​​非易失性内存快车 (NVMe)​​ 的出现标志着一个范式转变,这是一种从头开始为多核世界设计的架构。NVMe 的神来之笔是它对多个提交队列和完成队列对的支持。操作系统可以为每个核心创建一个私有的队列对。没有锁,没有争用。每个核心可以独立且并行地向自己的队列提交 I/O 请求。此外,使用一种称为 MSI-X 的机制,NVMe 设备可以将来自核心 iii 队列的请求的完成中断直接导向回核心 iii。这种设计完美地保留了 CPU 亲和性,确保提交请求的核心就是处理其完成的核心,从而最大化了缓存局部性并消除了唤醒等待线程所需的跨核 IPI。硬件和软件架构的这种优美协同演进,展示了对中断路径的深刻理解如何能够释放并行硬件的真正潜力。

内部的无形之舞:系统一致性与内务管理

并非所有中断都来自外部世界。在处理器系统内部,为了保持其连贯运行,发生着一场巨大而复杂的中断交响曲。其中最关键的一个是 ​​TLB 刷写 (Shootdown)​​。转换后备缓冲区 (TLB) 是一个用于虚拟到物理内存地址转换的每核缓存。当操作系统更改一个映射时——例如,通过移动一个内存页——它必须确保其他核心上的任何过时 TLB 条目都失效。它通过向所有受影响的核心广播一个 IPI 来实现这一点,命令它们“击落”旧的条目。

这个过程对于目标线程来说是一个“停止世界”的事件。它们被暂停,服务 IPI,使其 TLB 失效,并在一个同步屏障处等待,直到所有核心都确认完成。这个暂停的持续时间,通常是几微秒,直接、瞬时地打击了应用程序的响应时间。如果这些重映射事件频繁发生,累积效应可能导致整个机器的吞吐量显著下降。这些内部中断的成本揭示了内存管理子系统与整体系统性能之间的深层联系,而这一切都是通过 IPI 机制来协调的。

这种内部协调之舞延伸到操作系统的日常杂务中。考虑一个高性能服务器,其中关键的应用程序线程使用硬亲和性被绑定到“隔离”的核心上。目标是创建一个“圣所”,让这些线程可以在没有干扰的情况下运行。然而,系统仍然必须执行内务管理:垃圾回收、日志记录和性能监控。这些非关键任务必须使用软亲和性——一种偏好而非命令——小心地放置在剩余的“内务处理”核心上。

这种放置的艺术是一种巧妙的平衡行为。必须尊重 NUMA 局部性以避免像垃圾回收这样的任务进行缓慢的远程内存访问。人们可能会将日志记录线程与处理存储中断的核心放在一起,将监控线程与处理频繁网络中断的核心放在一起,以分摊唤醒成本。最重要的是,必须确保所有这些任务的总负载不会压垮内务处理核心,因为一个工作保守的调度器会毫不犹豫地将一个溢出的任务迁移到你宝贵的“隔离”核心之一,从而打破它的圣所。

这个圣所的脆弱性是深远的。软亲和性(用于线程)和 IRQ 亲和性(用于硬件中断)之间的区别至关重要。即使所有用户任务都远离一个隔离的核心,一个配置错误的单一中断——比如一个 stray 的计时器中断——也可能潜入并抢占一个对性能敏感的轮询应用程序。对于像数据平面开发套件 (DPDK) 这样的应用程序,它轮询网络设备的硬件环以避免中断开销,仅仅几百微秒的抢占就可能长到足以导致硬件环溢出,从而引起一连串的丢包。即使核心的平均处理能力远超数据包到达率,这种情况也会发生,这说明在低延迟计算的世界里,平均值具有误导性,而瞬时事件才是一切。

内核的平衡术:响应性与吞吐量

在操作系统内核深处,我们发现两个相互竞争的目标之间存在着根本性的张力:最大化吞吐量(处理尽可能多的工作)和保持响应性(确保用户任务不会饿死)。高频率的网络中断洪流使这种张力达到了沸点。如果内核只是在每个中断及其相关的软件中断(softirq)工作到达时就去处理它,一场足够强烈的风暴可能导致​​中断活锁​​,即 CPU 100% 的时间都在处理洪水,而用户空间应用程序则无限期地被剥夺 CPU 时间。

为了防止这种情况,Linux 内核采用了一种巧妙的平衡方法。它在硬件中断发生后立即处理软中断,但只处理到一定的工作量或时间预算。如果洪水继续,还有更多待处理的工作,它会将这些工作推迟到一个特殊的内核线程 ksoftirqd。这个线程在主调度器的控制下与用户进程竞争 CPU 时间。这种优雅的机制从理论上保证了系统不会被无限期地饿死。然而,如果传入工作的绝对量(数据包速率乘以每个数据包的处理时间)接近或超过 CPU 的能力,ksoftirqd 将消耗几乎所有可用的 CPU 周期,用户任务实际上仍将被饿死,几乎没有或完全没有时间运行。

这种权衡可以被提炼成一个更抽象的理论模型。想象一下,将 NNN 个与中断相关的任务突发分配到 mmm 个核心上。我们可以使用“运行至完成”模型,其中每个卸载的任务支付固定的 IPI 开销。或者我们可以使用“工作线程”模型,我们支付一次性的 IPI 成本来唤醒每个核心上的一个线程,然后该线程处理许多任务而无需进一步的开销。哪种更好?分析表明,没有单一的答案。工作线程模型通过分摊其启动成本,在大量工作(NNN 很大)时表现出色。而运行至完成模型对于较小的突发可能更有效。这表明了最优的中断处理策略与工作负载本身的性质密切相关,这一原则在并行计算理论中回响。

物理学的交响曲:功耗、热量与延迟

我们的旅程最终将中断的逻辑世界与功耗、热量和延迟的物理世界联系起来。我们在中断管理中所做的选择具有切实的的热力学后果。管理高中断率的一个关键技术是​​中断合并​​,即 NIC 将多个数据包事件捆绑成一个单一的硬件中断。这降低了 CPU 上每个数据包的开销。

然而,这个决策与现代电源管理功能如动态电压频率调整 (DVFS) 之间存在微妙的相互作用。一个创建大批量中断的策略可能导致 CPU 看到突然的、密集的工作爆发,促使其进入高功耗、高频率的“睿频”模式。虽然这能快速处理批次,但代价是功耗和热量产生的显著峰值。

一种更“热感知”的策略可能会选择更小、更频繁的批次。通过这样做,它可以使核心上的工作负载更平滑、更一致,使其能够保持在更节能的“正常”频率状态。这不仅降低了平均功耗,降低了芯片的稳态温度,而且还可能(有点反直觉地)导致更低的平均数据包延迟。默认的、激进的睿频策略可能会快速处理其大批次,但批次开始时的中断必须等待很长时间才能填满批次。而热感知策略使用较小的批次,确保没有哪个中断等待时间过长。这个优美的例子表明,中断处理不是一个孤立的数字问题;它是必须遵守物理定律的整体系统中的一个组成部分,其中管理能量和温度与管理周期和队列同等重要。

从网络的消防水带到良好冷却的处理器发出的安静嗡嗡声,多核中断管理的艺术是那根看不见的指挥棒。它是一门权衡和审慎平衡的学科,编排着一场信号的交响曲,这不仅决定了性能和吞吐量,还决定了现代计算的响应性、稳定性和物理效率。