【引子】“The Last CPU”(https://doi.org/10.1145/3458336.3465291),ACM上的这一篇论文非常有趣,核心思想是如果计算机的体系结构中没有了CPU,那么,操作系统又会是怎样的呢?......不敢私藏,分享给大家。失去了,才可能知道珍惜,试图减压的时候不妨去读一点论文,呵呵。
CPU的设计目的很单一,对存储的内存执行逻辑操作。协处理器(如IO通道处理器、中断控制器、定位点处理器)被添加到系统中,以提供CPU没有的功能,因为它们可以更快、更有效地执行某些特定的任务。它们通过允许CPU专注于应用程序逻辑和一般的系统功能来提高系统性能,而更简单的协同处理器则负责处理普通的任务。
对于高性能的应用程序而言,情况是完全相反的。可编程设备负责应用程序逻辑,而CPU只需要在这些任务中支持它们。众所周知,比起在通用CPU上运行的软件,应用程序专用的硬件可以执行更多的任务。加速器为许多应用程序,如图像识别、计算机视觉,关键值存储,数据仓库,大数据,深度学习,神经网络等,通常用于降低整体系统成本并提高性能超出通用指令集的能力。
在过去,只有程序中计算最密集的部分被添加到加速器上。最近,添加整个应用程序变得越来越普遍,比如SSD、GPU和FPGA等加速器,而CPU只需要用于初始设置和错误处理。我们相信系统已经发展到CPU是一个可以完全删除的附件
额外的加速器功能似乎是一个可行的替代方案。为了提高性能,已经对基本指令集进行了扩展,以对通用CPU提供类似加速器的能力,如矢量指令和加密。芯片现在正被用于进一步提高CPU的密度和降低制造成本。这些额外的功能使已经很复杂的CPU验证更加复杂化,需要更多的硅面积,这增加了CPU的基本成本和能源消耗,而这些功能可能从未使用过。此外,发布CPU的开发周期可能需要很多年时间,这意味着现有的系统不会容易升级或更改。CPU设计的通用方法已经开始达到一些困难的限制。
设计用于解决特殊问题的硬件组件正在变得普遍,它们可以做更多的任务,而且通常比通用CPU要快得多。从有效性来看,系统管理类似:一旦关键的处理任务被加载到其他硬件上,许多cpu就过于强大和昂贵。
以加速器为中心的集中控制系统,CPU只处理初始化、协调和错误处理等普通任务。分散的控制打破了对昂贵的通用CPU依赖,并可以提高性能。控制任务可以归结为简单的操作,可以处理在其他硬件上,与加速器和可编程设备的合作。
操作系统提供了三个关键功能:虚拟化,其中包括多路复用和地址转换、隔离和资源管理。这些功能从集中式操作系统内核转移到一个减少由自管理的硬件组成的传统模型。缺少的组件是设备相互协作所需的系统管理总线。正是这个总线来执行对安全敏感的配置,并负责任务生命周期管理(初始化、设置、拆卸)。引入系统管理总线作为一个专门的控制平面,结合自管理设备为一个更简单的数据平面,使从系统中完全删除CPU。操作系统仍然是控制平面,但不再运行在CPU上。
如果没有了CPU,计算机系统中的设备应该具有什么特性呢?
(1)设备必须是自管理的,设备必须管理它自己的内部状态。它必须公开它所提供的服务,并为服务的每个实例提供一个单独的上下文(多路复用),以确保应用程序之间的隔离。
(2)设备必须自主通信,设备必须能够发现它所需要的服务并请求它们,而不依赖于外部实体来控制它。
每个操作系统的功能——虚拟化(包括多路复用和地址转换)、隔离和资源管理,都是创建一个安全和可扩展的系统所必不可少的。打破对这些功能的责任,并将它们分配到设备和总线之间。因此,该系统由系统总线协调的自管理设备组成。下面的说明解释了如何在硬件组件之间划分操作系统的职责。
一个设备负责运行应用程序逻辑,并使用其他设备可能使用的一个或多个资源。一个设备可以使用任何资源类型的组合(物理内存、FPGA块、GPU核心、存储空间等),将每个服务都作为一个服务来公开。设备还可以提供系统维护的管理服务。例如,在内部存储其应用程序的设备必须公开一个可用于上传新的二进制镜像的加载服务。自我管理,意味着设备必须能够代表系统中的设备管理其资源的分配,并以标准化的方式公开它们。要做到这一点,每个设备必须实现逻辑来将其资源复用到多个实例中,提供实例之间的隔离并处理错误条件。
设备很可能会支持多个客户端或与任何特定服务的连接。例如,公开一个系统的智能SSD可以允许多个i由多个应用程序同时打开。在这种情况下,设备必须实现一种隔离机制,以防止实例之间的数据泄漏。设备可以在硬件或软件中实现对其资源的隔离。硬件中的细粒度资源分配已经在RDMA控制器及网卡等设备中实现,在这些设备中,硬件被划分为多个独立公开和控制的实例。
软件共享的方式在设备包含一个嵌入式CPU中很常见。无论实现方式如何,该设备都必须能够以一种系统和标准的方式公开其功能,以便希望使用该功能的其他设备易于使用。
VIRTIO是用于从虚拟机管理虚拟设备的标准化协议。VIRTIO可以为从自我管理的设备中公开资源提供一个理想的接口。与其他协议类似,它是基于一组内存描述符的单向队列的。硬件供应商已经开始生产符合VIRTIO标准的真正设备了,其主要优点是,许多与VIRTIO兼容的设备都可以使用一个驱动程序进行操作。以标准和一致的方式暴露所有资源和服务,简化了使用这些设备所需的逻辑。VIRTIO协议能够跨不同级别的抽象(nic、磁盘、控制台、套接字等)描述广泛的设备。所有这些都以一种一致的方式运作。
系统总线专门用于设备间的通信,系统总线(如图1所示)作为控制平面,使设备能够相互控制,但不携带数据。系统总线只提供了一种用于设备通信的机制,并且不包含任何策略,没有实体看到整个系统,也没有全局状态复制。该总线使设备能够以一种标准的方式通信其资源需求,并使设备能够广播它们的能力,以便其他设备可以发现它们。这是通过设备在总线上发送消息以请求诸如内存分配或打开文件之类的服务来实现的,作为一个特权设备运行,是维护虚拟化的机制。
当系统启动时,系统中的所有硬件设备都会经历一段初始化期间,在此期间它们可以执行自检。当设备确定它运行正常时,它将向系统总线发送一条消息,系统总线将记录它是活动的。之后,该设备将加载其应用程序,其中可能有很多。应用程序可以需要由其他设备提供的一个或多个服务。例如,网卡可能需要读取存储在SSD上的文件中的数据。在设备可以使用一个资源之前,它必须首先发现系统中哪些设备可以提供对该资源的访问。设备发现机制类似于即插即用套件或USB设备附件消息中的简单服务发现协议。
一个应用程序可以分布在许多设备上,但唯一标识它的是它的虚拟地址空间。与在当前部署的系统中一样,地址转换仍然是共享内存中数据隔离的基石。从安全的角度来看,设备负责自己的映射不是一个好主意,因为受损的设备可能会获得它未被授权的资源。因此,特权系统总线有责任通过更新页表来创建虚拟-物理映射。同样,也不能允许资源控制器直接访问其他设备的IO MMU,因为这将导致安全漏洞。相反,系统总线只有在特定资源的控制器指示设备时,系统总线更新设备的页表。
虚拟内存管理对于在同一应用程序的不同组件之间共享内存是必要的,同时保护该内存免受其他应用程序的影响。这在很大程度上是通过IOMMU来完成的,它可以从每个设备中访问物理内存,就像今天通常要做的那样。在分配内存时,系统总线提供了更新虚拟到物理映射的机制,但不提供策略。映射由内存控制器设置,内存控制器在内部为每个应用程序管理自己的分配表。这些映射被发送到系统总线,系统总线为请求设备的IO MMU编写适当的页表。
通过系统总线进行协调的设备将被要求遵守总线协议。这与遵守现有的总线协议没什么不同,例如PCIe。每个设备都需要符合最低限度的行为来与其他设备进行互操作和共享服务。操作系统通过控制器特殊的接口与设备进行通信,如USB控制器。设备和故障控制器已经通过设备上的硬件和软硬件参与了这些控制协议。这种通信将被更高层次的协议所取代,以直接从一个设备请求服务。系统总线协议不会比前面提到的许多现有控制协议有更复杂的计算量或者更复杂的逻辑。因此,目前存在的大多数设备和控制器将不需要对其硬件需求进行重大更改,以支持系统总线。
接下来,可能需要互连的两个不同的功能:内存访问数据平面和设备连接控制平面。从系统设计和性能的角度来看,这些功能应该是分开的。在传统的系统中,CPU负责在初始化过程中设置地址空间。由于我们不能依赖于CPU,所以在设置虚拟地址空间之前,必须有一个独立的寻址设备的方法。PCIe通过通过物理地址(总线、设备、功能)寻址设备,通过标准的节点空间和BAR区域提供内存访问和一定程度的设备协调,部分地实现了这两个功能。如果大多数设备将支持多个虚拟地址空间(每个应用程序一个),那么他们必须能够选择每个内存操作所使用的虚拟地址空间。内存总线必须具有高吞吐量和低延迟,而系统管理总线则不需要。
另一方面,系统管理总线必须能够处理消息,这样它才能代表应用程序更新管理表。虽然设计一个包含这两种功能(高速内存访问和消息解码)的总线并不是不可能的,但我们没有看到一个令人信服的理由来组合它们。有许多现有的系统互连似乎是很好的候选者,如PCIe、CCIX、openCAPI 和CXL。
通知是一种让设备表示需要注意的方法。这可能是由正常操作引起的,例如通知一些请求的数据已经准备好了。它也可以用来表示一个错误的条件,例如作为由于无效的虚拟地址而导致的DMA事务失败。对CPU的通知通常使用中断发送,但也可以通过互连作为内存写入到一个特殊地址的方式发送。这类似于用于实现PCI的MSI(消息信号中断)的方法。
在没有CPU的系统中,缓存一致性具有不同的意义。高速缓存的目的是通过避免对主存的昂贵访问来提高CPU的性能。将缓存视为属于内存层次结构是很方便的,这掩盖了大多数缓存驻留在同一个物理包中的事实。因此,对于没有CPU的系统,必须仔细考虑缓存和缓存一致性的位置和目的。由于缓存是设备的私有的,如果设备只使用内存与其他设备共享数据,缓存将不会提供太多好处。设备和应用程序肯定会继续使用大量的RAM,并将大量利用设备中的缓存层次结构(例如GPU)。然而,缓存一致性只在依赖于不同处理单元之间的隐式内存共享的编程模型中。许多分布式系统都依赖于显式的消息传递,并完全丢弃一致性。第2节中提到的大多数互连都支持缓存一致性消息,但不需要它们。简而言之,每个设备都可以根据其硬件能力和应用程序的需要来选择是否参与系统的高速缓存一致性。
某些智能网卡和智能ssd可以相对容易地通过监控软件进行增强,没有的硬件组件是系统总线。为了完成这个无CPU 操作系统,需要一个独立的内存控制器和互连控制器,类似于英特尔的内存控制器Hub。
畅想一下,可以在CPU上运行的软件中模拟系统总线的操作。每个自我管理的设备都会像往常一样,从系统总线发送和接收消息,但这些消息将通过共享内存通过隧道传输到软件模拟器。该模拟器会拦截任何内存分配消息,并相应地重新编程IO MMU。模拟器还需要扮演任何还无法嵌入到设备中的资源监视器的角色:例如,内存控制器。
构建一个模拟的无CPU系统,将允许回答关于这类系统的可行性、安全性和性能问题。
为了尝试描述系统如何工作,描述了一个假设的键值存储应用程序将如何在没有CPU的系统上工作。数据(键和值)存储在一个由智能SSD托管的文件中,而操作(获取、插入、更新等),在智能网卡中进行处理。网卡通过监听套接字或RDMA,通过网络向其他机器公开一个接口连接。
图2显示了初始化序列,在NIC中运行的KVS应用程序连接到SSD以访问其数据文件。网卡通过系统总线发送一条广播消息(包含文件名),以发现是哪个存储服务拥有该文件。SSD响应说它可以为该文件访问服务。网卡发送请求以打开服务(包括授权令牌)以访问该文件。SSD响应了连接详细信息和所需的共享内存量。网卡向内存控制器(包括虚拟地址)发送一个请求,要求其分配共享内存。在收到来自内存的响应后,系统总线对属于网卡的IO MMU进行编程,允许其访问指定的虚拟地址的共享内存。网卡向系统总线发送另一条消息,以授予SSD对共享内存的访问权限。然后,网卡可以通过使用虚拟地址编程SSD中的VIRTIO队列来建立连接。
IO MMU通过将设备访问的所有内存访问转换为虚拟内存空间来保护内存。要创建共享内存区域,必须为IO MMU进行编程,以便将两个设备使用的虚拟地址映射到相同的物理地址。在带有CPU的系统中,操作系统内核负责内存管理,因为它包含每个进程的地址空间的映射,并且能够执行特权任务。一个应用程序将调用一个系统调用上为内核创建一个共享的内存映射。
在没有CPU的情况下,内存控制器负责跟踪每个设备的物理内存分配,可以更新映射的系统总线。设备发送一条控制消息,而不是系统调用。一旦该操作被内存控制器授权,系统总线就会执行该操作。对内存区域的访问可以由拥有该区域的设备授予给另一个设备,但必须首先由内存控制器授权。
操作系统是非常复杂的系统,无CPU 的操作系统也不例外。
如果需要细粒度的访问控制,则访问控制服务可以由智能存储控制器提供,例如智能SSD。这将大致相当于在Linux上的“登录”程序和“密码”文件。对单个文件的访问控制由系统服务在提供该服务的设备上实现。希望打开文件的用户将通过控制台应用程序输入命令,该应用程序将在从文件系统服务请求文件时使用该用户的标识符。类似地,加载服务(将微代码、加密软件或应用程序代码加载到设备)也可以在替换敏感数据之前使用相同的身份验证服务。
将应用程序加载到各种加速器之后,如何解决处理编程中的错误呢?
现有系统依赖于CPU和操作系统来处理各种不指定的错误。对于无CPU 的操作系统,必须更精确地确定将发生的错误类型,以及如何处理它们。可恢复的错误是指那些不需要重置设备的错误。当转换硬件无法绑定映射时,或者当试图访问没有正确权限的内存时,就会导致页错误。在一个没有CPU的操作系统中,IO MMU会将任何故障发送到其附加的设备上。每个设备都将负责适当地处理自己的故障,重置服务或停止应用程序。故障模型并不比在具有集中式CPU的系统中更糟糕。主要的不同是错误处理的责任已经转移到设备本身,而不是一个外部实体。
类似地,如果资源出现致命错误,则该设备将负责处理该错误本身。它必须向使用该资源的任何使用者发送消息,然后重置该资源,应用程序的逻辑要负责从这个场景中恢复。如果整个设备发生故障,则资源总线必须向系统中可能正在使用该故障设备的资源的所有其他设备发送消息。总线还可以向故障设备发送重置信号,试图重新启动它。
应用程序可以在正常操作期间写入日志文件。系统操作员可能希望定期查看这些日志,以收集统计数据或调整一些参数。这样的机器很可能会被部署为数据中心中的服务器,而不会有一个本地控制台。如果将许多这样的系统部署在一个数据中心中,那么远程操作将是一个不错的选择,因为一个远程控制台可以用于管理许多无CPU的系统,可以通过网络由另一台机器通过远程访问服务查看这些日志。用户身份验证可以通过运行在任何设备上的身份验证服务来执行。
对于一个不寻常的操作系统来说,最大的问题可能是如何对它进行编程。需要注意的是,编写一个应用程序,以在可编程设备上运行,该设备可以使用来自一个或多个其他设备的服务。这些应用程序是在具有开发环境的机器上开发的。由于每个设备都可以有一个不同指令集或实现语言,因此将需要多个工具链。
然而,在许多情况下,开发过程将只针对一个设备。以上面的KV-store应用程序为例,所有应用程序逻辑都将被编译为在smartNIC上运行。smartNIC的开发环境将包括一个库,它封装了系统总线的功能,并提供服务发现、资源分配等功能。这取决于系统中的其他设备(SSD、内存控制器等)能够以适当的方式公开所需的资源。
保护措施依赖于虚拟内存来防止对另一个应用程序的内存做未经授权的访问。设备的自身来提供在运行在同一设备上的应用程序之间的隔离。在设计诸如文件系统等常用服务时,肯定还会出现其他安全问题。这与为NFS服务设计安全模型没什么不同,向远程设备公开了文件的抽象。
对更高性能的需求正在推动系统设计向专门的硬件方向发展。单个设备正在快速发展以进行自我管理,放松了对CPU和传统操作系统的依赖。
无CPU的操作系统仅仅是一个思想实验,一个系统中完全删除CPU。如此剧烈的变化迫使我们以一种新的方式考虑系统设计,并就如何管理这样的一个系统产生影响。然而,如果CPU不再是现有操作系统的中心组件,会从根本上改变软件在CPU上的编写方式吗?当然,实际上有些问题在CPU上更容易解决。
联系客服