
在虚拟化世界中,在单一物理机器上运行多个相互隔离的操作系统的能力彻底改变了计算领域。然而,这种强大的抽象能力通常伴随着性能开销,尤其是在虚拟机(VM)需要与高速外围设备交互时。传统的在软件中模拟硬件的方法会造成严重的 I/O 瓶颈,从而限制了要求苛刻的应用的潜力。我们如何在不损害虚拟化安全基石——隔离性——的前提下,赋予虚拟机直接访问硬件的原始速度?
本文将探讨设备直通(device passthrough)技术,这是一种功能强大的技术,通过在虚拟机和物理设备之间建立一个安全、高性能的桥梁,直接解决了这一挑战。它解决了直接访问硬件所固有的安全风险,展示了现代系统如何在不牺牲系统完整性的情况下提供接近本机的性能。在接下来的章节中,您将深入了解这项至关重要的虚拟化技术。原理与机制一章将解构设备直通的工作原理,介绍直接内存访问(DMA)和输入/输出内存管理单元(IOMMU)的关键作用。随后,应用与跨学科联系一章将展示这些原理在现实世界中的应用,从构建更快的云平台到设计更安全的汽车。
要真正领略设备直通的魅力,我们必须首先退后一步,看看计算机是如何工作的。计算机的核心是一个聪明但常常超负荷工作的中央处理器(CPU)和一个被称为内存的庞大信息库。但一台只能自言自语的计算机并没有多大用处。它需要通过网络、显示器和存储设备与外部世界互动。这就是外围设备的工作。
想象一下,一个网卡正在接收来自互联网的大量数据。如果 CPU 必须亲自将每一个字节从网卡护送到内存中的最终目的地,它将没有时间做任何其他事情。整个系统都会陷入停顿。为了解决这个问题,工程师们赋予了外围设备一项奇妙的超能力:直接内存访问(DMA)。
DMA 允许设备直接对计算机主存进行读写数据,完全绕过 CPU。CPU 只需告诉设备:“这里是内存中我希望你发送的一块数据”,或者“当新数据到达时,请将其放入此内存缓冲区”,然后就可以处理其他事务。设备会自己完成整个传输过程。
在一个操作系统运行在裸机上的简单世界里,这是一种优美而高效的安排。但在虚拟化世界中,一台物理机承载着许多独立的虚拟机(VM),这种超能力就变成了一个可怕的安全风险。如果你让一个虚拟机直接控制一个设备,那么该虚拟机内部的恶意程序就可以命令设备利用其 DMA 能力来覆写 hypervisor 的内存,或者窥探另一个虚拟机的数据,有什么能阻止它呢?什么也没有。一个拥有不受限制 DMA 能力的设备就是一个巨大的安全漏洞。
我们如何在不危及整个系统的情况下,赋予设备这种惊人的性能优势呢?我们需要一个守门人。我们故事中的英雄登场了:输入/输出内存管理单元(IOMMU)。
IOMMU 是一块位于 I/O 设备和主存之间的硬件。它的工作类似于 CPU 自身的内存管理单元(MMU),但它管理的不是 CPU 的内存视图,而是设备的内存视图。当设备向某个内存地址发起 DMA 请求时,IOMMU 会拦截该请求。它会在一个由可信的 hypervisor 编程的特殊表中查找该地址,并进行转换。
可以把它想象成银行的保安。客户虚拟机的驱动程序可能会告诉其设备:“将此数据写入 123 号保险箱。”然而,设备并不知道保险库的真实布局。它将其对 123 号保险箱的请求发送给 IOMMU。我们的保安 IOMMU 会查阅由银行经理(hypervisor)提供给它的账本。账本上写着:“对于这个客户,‘123 号保险箱’实际上对应于真实的 8675 号保险库位置,并且他们只被允许访问 8675 到 8690 号位置。” IOMMU 转换地址并确保访问在允许的范围内。如果设备试图访问 ‘500 号保险箱’,一个在其账本中没有有效映射的地址,IOMMU 就会阻止该请求并发出警报(产生一个故障)。
这种优雅的硬件机制是安全设备直通的基石。它将设备强大的 DMA 能力严格限制在分配给其客户虚拟机的内存页内。它确保了即使客户机是恶意的,也无法利用设备逃出其虚拟监狱。这种分离至关重要:CPU 的内存访问由其 MMU(使用嵌套页表等结构)监管,而设备的内存访问则由 IOMMU 监管。这两个系统并行工作,以提供全面的隔离。
有了 IOMMU 提供的安全网,我们现在可以考虑让虚拟机直接访问物理设备了。这种被称为设备直通的技术是虚拟化世界中 I/O 性能的巅峰。但它并非唯一的选择。事实上,它处于性能、灵活性和隔离性之间权衡的一端。
完全设备仿真: 在这个光谱的一端,hypervisor 可以完全在软件中模拟一个设备。当客户虚拟机认为它在与网卡通信时,它实际上只是在发出被 hypervisor 捕获的请求。然后,hypervisor 解释这些请求,并在真实硬件上执行相应的操作。这提供了最强的隔离——客户机对任何硬件都没有访问权限。然而,这种软件解释的速度非常慢,使其不适用于高性能任务。 有趣的是,这个软件层有时可以提供更高的数据完整性。如果底层主机文件系统是健壮的,它可以保护客户机免受廉价、普通的 USB 驱动器在断电期间的不稳定行为的影响——这是直通技术无法提供的保护。
半虚拟化(例如 virtio): 这是一种协作式的中间方案。客户机操作系统“意识到”自己被虚拟化,并使用一个特殊的高效软件通道与 hypervisor 通信。Hypervisor 仍然仲裁对物理设备的访问,但通信过程被简化了。这提供了比完全仿真好得多的性能,并保留了 hypervisor 控制的许多优点,例如在虚拟机之间公平调度网络流量的能力以及执行实时迁移的关键能力。
设备直通(例如 SR-IOV): 这是追求极致性能的选项。Hypervisor 几乎完全从数据路径中退出。使用像单根 I/O 虚拟化(SR-IOV)这样的技术,一个物理设备可以呈现出多个“虚拟功能”(VF),每个 VF 都可以直通给不同的虚拟机。客户机驱动程序直接与硬件 VF 通信。对于像需要每秒 90 帧的虚拟现实应用这样的高要求工作负载,其他方法的开销实在太高;直通是唯一可行的选择。 虚拟机获得了接近本机的性能,但这是有代价的。Hypervisor 失去了强制执行细粒度网络策略的能力,并且出现了一个重大挑战:设备的状态现在与一块物理硬件绑定在一起。
实现真正的本机性能不仅仅涉及数据路径。设备需要引起 CPU 的注意,虚拟机也需要是可管理的。在这里,直通技术的美丽简洁性也暴露了其尖锐的棱角。
设备通过发送中断来通知 CPU。在一个纯仿真的世界里,这涉及到一次代价高昂的“VM exit”,即控制权从客户机转移到 hypervisor,然后 hypervisor 再将一个虚拟中断注入回客户机。这会增加显著的延迟。通过直通技术,我们可以利用像消息信号中断(MSI-X)这样的硬件特性,并结合 IOMMU 中的中断重映射。MSI-X 只是一种特殊的 DMA 写入。IOMMU 可以重映射这次写入,以特定客户机的虚拟 CPU 为目标,并且借助一种称为posted interrupts的特性,这可以在完全没有 VM exit 的情况下发生。硬件将通知直接传递给客户机,为我们提供了一条与快速数据路径相匹配的极速控制路径。
但这种与硬件的紧密绑定是有代价的:灵活性。虚拟化的杀手级特性之一是实时迁移,即在没有停机时间的情况下将正在运行的虚拟机从一个物理主机移动到另一个物理主机的能力。这涉及到复制虚拟机的内存和 CPU 状态。但是,直通网卡的状态怎么办呢?它的配置、活动的连接过滤器、内部缓冲区——所有这些状态都存在于源主机的物理芯片内部。Hypervisor 无法简单地读取它。除非设备硬件本身提供一种特殊的机制来保存和恢复其状态,否则实时迁移是不可能的。常见但复杂的解决方法是一种精巧的操作:从虚拟机热拔插物理设备,热插拔一个临时的半虚拟化设备,迁移虚拟机,然后在目标主机上反向执行此过程。
此外,物理世界不容忽视。现代服务器通常采用非统一内存访问(NUMA)架构,拥有多个插槽,每个插槽都有自己的本地内存。访问远程插槽上的内存速度较慢。如果一个虚拟机的 CPU 在插槽 B 上运行,但其直通的网卡物理上插在插槽 A 上,性能损失将不可避免。从设备到虚拟机内存的每一次 DMA 都必须跨越插槽间的连接。从设备到虚拟机 CPU 的每一次中断也必须跨越同一个连接。正确的性能调优需要 NUMA 感知的布局,将虚拟机的 CPU、其内存及其直通设备共同放置在同一个物理插槽上。虚拟世界仍然受制于物理世界。
IOMMU 是一个强大的守护者,但它的保护能力取决于 hypervisor 赋予它的规则。在这个复杂系统中,一个单一的 bug 或配置错误就可能导致隔离的彻底崩溃。想象以下几种情况:
过于宽松的映射: hypervisor 中的一个 bug 可能会意外地在 IOMMU 中创建一个比预期大得多的“超级页”映射,从而暴露出包含 hypervisor 自身代码的物理内存区域。恶意的客户机随后可以编程其设备对该区域进行 DMA,从而控制整个机器。
过时的转换: 为了提高速度,IOMMU(以及设备本身)会缓存地址转换。如果 hypervisor 从客户机取消映射一个内存页并将其重新分配给自己的内核,它必须通知 IOMMU 刷新该过时的缓存条目。如果未能及时这样做,客户机的设备可能会继续使用其旧的、缓存的权限对该页面进行 DMA,从而破坏敏感的主机数据。这是一个典型的“检查时到使用时”(Time-of-Check-to-Time-of-Use, TOCTOU)攻击。
被遗忘的恒等映射: 一些系统为 IOMMU 提供了一种特殊的“恒等映射”模式,在这种模式下,它只是简单地传递地址而不进行转换。如果为直通设备错误地启用了此模式,客户机便可以写入其选择的任何物理地址,从而使所有保护措施都变得毫无意义。
这些例子表明,虽然原理很优雅,但它们的实现需要格外小心。在这些系统中,安全不是一道单一的墙,而是一系列精心协调的防御措施。
正当你认为自己已经完全理解时,虚拟化世界又增加了一个层次。如果你的客户虚拟机本身就是一个 hypervisor,运行着它自己的一套“孙子”虚拟机呢?这就是嵌套虚拟化。现在,假设这个客户 hypervisor()想把一个物理设备直通给它自己的客户机()。
中的驱动程序只知道它自己的“物理”地址()。然而,设备需要一个最终的主机物理地址()。这需要一个两阶段的转换:首先从 转换到 的物理地址空间(),然后从 转换到真正的主机物理地址()。如何安全地做到这一点呢?
答案再次在于扩展我们的原理。要么硬件必须提供一个能够直接执行这种两阶段转换的嵌套 IOMMU,要么顶级 hypervisor()必须捕获并模拟 对 IOMMU 进行编程的所有尝试,在软件中组合这些转换,为真实的硬件 IOMMU 构建一个“影子”映射。 这种优美的递归展示了底层概念的力量和统一性。无论是访问共享资源的仲裁这个基本问题,还是硬件强制、软件管理的转换层这个解决方案,无论你深入探索到何种程度,它们都一再适用。
既然我们已经拆解了设备直通的内部机制,并看到了输入/输出内存管理单元(IOMMU)的齿轮如何转动,我们可以提出一个更令人兴奋的问题:它究竟有何用途?我们可以用这个新工具构建出怎样奇妙的机器?你可能会欣喜地发现,答案并不仅限于计算领域的一个狭窄角落。这个看似简单的、让虚拟机在受监督的情况下直接访问一块硬件的想法,在许多领域都产生了共鸣,从驱动云计算的喧嚣数据中心,到引导我们汽车的静默关键计算机。这是一个绝佳的例子,说明一个单一、执行良好的概念可以产生多么强大而多样的影响。
设备直通最直接、最明显的好处是性能。在计算世界中,每一层软件、每一次转换、每一次间接访问都会增加开销。传统仿真形式的虚拟化,就像试图通过一长串翻译进行对话;信息最终能够传达,但速度缓慢,且在翻译中浪费了大量精力。
考虑从一个现代、快如闪电的非易失性内存主机控制器接口(NVMe)存储驱动器中读取文件。在一个完全仿真的系统中,客户机的请求从其应用程序出发,穿过其自身的内核,到达一个虚拟设备驱动程序,然后陷入(trap into)hypervisor。Hypervisor 模拟硬件,转换请求并将其传递给主机内核,最终由主机内核与物理设备通信。设备直通则短路了这整个链条。这就像给了客户机一条专线,让它能够用硬件的母语与之对话。其结果是中央处理器(CPU)开销急剧减少,延迟显著降低,使虚拟机能够实现接近裸机系统的 I/O 性能。
在响应能力至关重要的领域,对速度的需求更为迫切。以交互式 3D 图形和游戏为例。为了在 的显示器上获得流畅的体验,必须每 毫秒渲染一帧新画面。这是一个极其紧张的“延迟预算”。虚拟化堆栈引入的每一微秒开销都会侵蚀这个预算,可能将流畅的体验变成卡顿的混乱。在这里,直通技术(通常以一种“中介”形式,即由特殊驱动程序帮助协调访问)允许虚拟机以最小的延迟指挥强大的图形处理单元(GPU),从而使高性能虚拟化工作站和云游戏成为现实。
认识到这一需求,硬件制造商自己也接受了直通的理念。像单根 I/O 虚拟化(SR-IOV)这样的技术本质上是直接内置于硬件中的直通。例如,一个物理网卡可以呈现为数十个独立的“虚拟功能”,每个功能都可以直接透传给不同的虚拟机。这是现代云基础设施的基石,使租户能够获得他们处理高要求工作负载所需的裸机网络性能。
然而,设备直通最深远的应用或许不在于让事物变得更快,而在于让它们变得更安全。这可能看起来有悖常理——给予虚拟机对硬件的直接访问权限难道不会产生安全风险吗?答案在于 IOMMU 提供的周密监督。
要理解这一点,让我们比较两种流行的虚拟化形式:容器和虚拟机。想象你是一个云服务提供商,在一台物理服务器上为无数租户充当房东。给一个容器“直通”设备访问权限,就像给一个租户一把通往大楼主公用设施控制室的钥匙。因为容器共享主机的内核,你正在将主机自己的设备驱动程序——一个具有巨大攻击面的复杂软件——直接暴露给租户。你必须完全信任他们,相信他们不会以影响其他租户或大楼本身的方式乱动开关。
而带有 IOMMU 的虚拟机直通则完全是另一回事。这就像给每个租户他们自己私有的、上锁的杂物间。IOMMU 就是那把锁。它确保来自租户设备的任何直接内存访问(DMA)只能触及属于该租户虚拟机的内存。Hypervisor 扮演着大楼管理员的角色,持有唯一的万能钥匙并制定规则。租户可以在他们自己的虚拟机里运行任何他们想要的代码,甚至是恶意的驱动程序;IOMMU 硬件可以防止损害蔓延出他们自己的四壁。
但如果这个守护者粗心大意会发生什么?一个配置了宽泛的“恒等映射”孔径的 IOMMU,就像一个宣告“对于前几个吉字节内的任何地址,无需检查,直接放行!”的警卫。由于主机的内核通常位于这些低物理地址,客户虚拟机可以简单地指示其设备读取主机的私有内存,从而彻底打破隔离边界。这就是为什么正确、安全地使用 IOMMU 需要为客户机有权使用的内存创建明确的、逐页的权限,且仅限于这些内存。
在具有许多设备的复杂系统中,情况变得更加复杂。即使大门口有警惕的守卫,聪明的对手也能找到其他途径。一些 PCIe 交换机允许“点对点”DMA,即两个设备可以直接通信,而它们的流量根本不会“向上”到达 IOMMU 守卫所在的根联合体。这就像两个本应隔离的囚犯通过共用的通风井传递秘密纸条。一个恶意的虚拟机可以利用它的设备直接攻击另一个虚拟机的设备。为了防止这种情况,系统使用了另一种称为访问控制服务(ACS)的硬件特性,它就像走廊里的一系列单向门,迫使所有此类流量向上游移动,接受 IOMMU 的检查。
然而,有时设备的本质就与这种完美的、排他性的隔离模型相悖。考虑一个可信平台模块(TPM),这是一个充当整个系统物理信任根的硬件芯片。一台机器通常只有一个。如果你把它直通给单个虚拟机,主机和所有其他虚拟机都将失去它的保护。这就像把王国唯一真正的王冠送给了一位王子。在这里,纯粹的直通失败了,需要一种更细致的方法:一个软件虚拟 TPM(vTPM),它为每个虚拟机创建一个仿真的、私有的 TPM,同时将其自身的秘密锚定在唯一的、共享的物理 TPM 中。这是一个绝妙的折衷方案,展示了隔离与共享资源需求之间的持续张力。
速度和隔离的原则在嵌入式和安全关键型系统的世界里找到了绝佳的结合点。思考一下现代汽车中的中央计算机。这是一个“混合关键性”的世界,在单个芯片上运行着两个截然不同的领域:关乎生死的车辆控制领域(制动、发动机管理、转向)和变化无常、尽力而为的信息娱乐领域(音乐、地图、网页浏览)。音乐播放器中的一个错误或崩溃绝不能在任何情况下影响制动系统。
设备直通是实现这种健壮分区的关键使能技术。Hypervisor 将系统一分为二。高关键性控制虚拟机被分配专用的 CPU 核心,并且至关重要的是,被赋予对控制器局域网(CAN)总线——汽车的数字神经系统——的直接直通访问权限。信息娱乐虚拟机被锁定在自己的分区中,拥有自己的资源,没有任何可能干扰关键组件的路径。在这里,直通不是为了性能而设的奢侈品;它是为了安全性和确定性而存在的架构必需品。
尽管功能强大,这种直接的物理链接也带来了其自身引人入胜的挑战,推动了计算机科学的前沿。虚拟化的魔术之一是“实时迁移”,即在没有可感知停机时间的情况下将运行中的虚拟机从一个物理主机移动到另一个。但当虚拟机通过直通物理地绑定到一个设备上时,会发生什么呢?用于 DMA 的内存页被“钉住”(pinned),就像一个将虚拟机固定在原地的锚。
你不能在没有警告的情况下简单地拔起锚;必须先告知设备放手。这催生了优雅的协作协议的发展。Hypervisor 向客户机的驱动程序发送一个“半虚拟化提示”——一条礼貌的软件消息——请求它静默设备并为迁移做准备。这是一个绝佳的例子,展示了软件对话如何解决硬物理约束,融合了硬件辅助和半虚拟化技术。
让我们以一个真正拓展思维的想法来结束:时间旅行调试。如何为一个计算机程序建造一台时间机器?对于一个自包含的程序,想法很简单:记录每一个外部输入,要重放执行过程,只需在相同的逻辑时间提供相同的输入。但是,一个带有直通设备的虚拟机在不断地与混乱、不确定的物理世界互动。设备是不可预测输入的消防水管。为了实现确定性重放——建造一台真正的时间机器——hypervisor 必须成为一个偏执的记录员。它必须记录下 MMIO 读取返回的每一个值,设备 DMA 操作写入的每一个字节,以及每个事件发生的确切逻辑时间(例如,虚拟机的已退役指令数)。重放执行过程意味着将这个巨大的事件日志文件反馈给虚拟机,并与它的内部时钟完美同步。这项艰巨的任务揭示了 I/O 的基本性质,即作为 CPU 的确定性世界与外部不可预测宇宙之间的桥梁。
从榨取最后一滴性能到构建坚不可摧的数字堡垒,再到确保我们车辆的安全,设备直通展示了科学和工程中一个反复出现的主题:一个简单、优雅的原则,当与周密的监督相结合时,可以产生一系列惊人丰富和强大的结果。它提醒我们,虚拟化的艺术不仅仅在于创造幻象,更在于在逻辑与物理之间锻造新的、强大的联系。