背景
以往对多核实时操作系统的研究主要集中在多核处理器上任务集的可调度性和响应时间分析。
同时,许多研究人员声称,在不久的将来,高端嵌入式系统还将包括高性能并行应用程序,以支持复杂的任务,如车辆的自动驾驶。目前主流的嵌入式多核处理器由于核数较少,不适合于并行应用。近年来,已经发布了几种面向未来嵌入式系统的现成多核处理器,这些处理器包含数十个(甚至数百个)核。
然而,由于传统RTOS的设计目的不是为这些应用程序提供高可扩展性,因此它是否能够在许多核心处理器上实现并行应用程序的良好扩展尚不清楚。
Linux实际上可以在许多具有数十个内核的核心处理器上(或至少在一台48核的机器上)提供良好的可扩展性。
本文工作
在本文中,我们重点研究了传统多核RTOS在少于100核的多核处理器上的可扩展性。
1、将一个称为TOPPERS/FMP内核的传统多核RTOS内核移植到72核TILE-Gx72嵌入式多核处理器。
2、通过与Linux 4.5进行比较,发现了RTOS中的几个瓶颈。这些问题实际上已经在Linux中得到了解决,我们已经提出了在RTOS中避免这些问题的方法。
3、最后,我们通过PARSEC基准测试套件评估和分析了RTOS和Linux的可扩展性。
如果RTOS在并行基准测试中显示出与Linux相比接近(或更好)的可伸缩性,那么我们可以说传统RTOS至少潜在地适合在嵌入式多核处理器上扩展并行应用程序。
FMP与Linux的比较
1、系统设计:静态与动态
对于大多数嵌入式系统,所需的和可用的资源是预先确定的,因为这些系统设计用于执行某些特定任务。FMP是静态设计的,所有的内核对象(例如任务、信号量、数据队列)是静态配置的,在配置文件中定义,编译时由配置文件生成。每个内核对象都与一个ID相关联,以便通过系统调用进行访问。好处:由于FMP中内核对象的数量在运行时是固定的,因此它们可以简单地存储在数组中,并使用索引作为ID进行引用。可以实现更小的占用空间、更好的实时性能和更高的可靠性。缺点:灵活性有限。
Linux是动态设计的,以提供通用系统的灵活性。内核对象是在系统运行时调用内核提供的API创建和初始化的。通常,这些API会返回为该对象动态分配的数据结构指针。好处:Linux中内核对象的数据结构使用动态分配内存块的链表进行管理,因为它们的数量被认为是无限的。缺点:与静态设计的直接实现相比,动态设计引入的这种复杂性会导致更高的开销。
2、基本服务和设备驱动
从应用程序开发人员的角度来看,操作系统内核主要由基本服务和设备驱动程序组成,它们的功能通常作为系统调用提供。
通常,在FMP中访问服务的开销较低,而Linux经过优化可以实现更好的吞吐量和可伸缩性。
由于嵌入式系统的硬件对于特定的应用领域是高度专业化的,内核很难假设应该提供什么设备驱动程序。FMP本身只包括最少必要的设备驱动程序,如定时器、中断控制器和串行端口的驱动程序(如果使用syslog)。其他设备由在内核之上实现的可选中间件组件支持。开发人员需要使用每个中间件的专用API来控制相应的设备。
相反,Linux作为一个GPOS内核,必须支持大多数常用设备,并且提供一种通用的访问方法。驱动程序是内核中的模块,通过系统调用接口与应用程序交互。更具体地说,Linux中的设备被抽象为称为设备文件的特殊文件,可以通过文件I/O系统调用(如write())进行访问。
3、任务调度机制
多核系统中的调度目标是确定任务如何映射到不同的核,以及CPU资源如何分配到每个核上的任务,以实现吞吐量最大化或响应时间最小化等目标。
在FMP中,用户应用程序中的任务通过一种称为循环调度然后FIFO(RtFIFO)的方法进行调度。任务在创建时以循环方式分配给核心。每个核心将依次获得一个任务,直到分配完所有任务。同一核心上的任务将由默认的固定优先级抢占式调度器处理。所有任务都设置为相同的优先级,因此它们将以FIFO方式在每个核心上提供服务。
默认情况下,Linux使用完全公平调度程序(CFS),其目的是最大限度地提高总体CPU利用率。CFS定期运行复杂的负载平衡算法,以保持所有核心的运行队列大致平衡。由于比较两个具有不同调度规则的运行时系统是不公平的,我们也在Linux上实现了RtFIFO方法。
4、内存管理
大多数操作系统内核都支持内存管理,包括静态操作系统内核(如FMP),它的功能主要包括两部分:内核中的对象管理和用户应用程序中的数据管理。
对于内核对象,FMP和Linux用于管理它们的机制非常相似。尽管FMP不支持内存的动态分配,但它允许用户静态地定义对象,如具有预定最大数量的固定大小块的内存池。因此,使用对象池设计模式可以轻松实现内核对象的动态管理。在Linux中,称为slab allocation的机制也基于对象池,用于内核对象的高效内存分配。
页表管理:
对于像TILE-Gx72这样使用分页MMU的处理器,用户应用程序数据的动态分配在技术上意味着将页表条目与可用物理内存相关联。在FMP中,所有页表都是静态生成的,未使用的物理内存定义为一个池,可以从用户应用程序访问该池。也就是说,从页面的角度来看,所有可用的物理内存已经提前分配给用户应用程序。预先分配的可用内存池可以由内存分配器中间件进行逻辑管理,并且无需在运行时动态更改页表。
同时,Linux有一个极其复杂的页表管理实现,具有高级功能,包括按需分页和交换。在某些情况下,动态修改页表可能是一个瓶颈,多年来一直在积极研究和优化。
因此,如果应用程序经常从内核请求内存管理服务,那么它在FMP上的性能可能比Linux更好。
5、内核锁
是一种多核操作系统中支持核间同步和通信的基本机制。内核中锁模型的粒度对总体吞吐量有很大影响,因为几乎所有共享资源都需要锁来保护。
在FMP中,有三个粒度级别可供选择:巨锁(G KLOCK)、处理器锁(P KLOCK)和细粒度锁(F KLOCK)。在巨锁模式下,所有内核对象共享一个单独的全局锁,因此像系统调用这样的内核服务只能串行访问。此模式需要最少的内存空间,但具有最高的资源争用。在处理器锁模式下,同一核上的内核对象共享一个锁,因此可以并发处理不同核上的内核服务请求。在细粒度锁模式下,每个内核对象都有自己的锁,因此可以尽可能并行地提供内核服务。此模式具有最低的资源争用,但如果存在大量内核对象,则会比其他模式占用更多内存。
由于我们的实验环境有大量可用内存,我们使用细粒度锁模式来最大化并行性。
当前Linux内核中的锁定粒度比FMP中的细粒度锁定模式略粗,因为Linux更复杂,并且包含许多由多个内核对象组成的组件。
TAS和TATAS自旋锁用于FMP等嵌入式系统的RTOS中,该自旋锁可以在几个核心的嵌入式系统上提供优异的性能,但在许多核心的处理器上吞吐量可能会急剧下降。
在Linux中,在TILE-Gx72处理器上使用退避自旋锁(a ticket spinlock with exponential backoff)。退避自旋锁也不可扩展,但被认为比TAS和TATAS自旋锁有更好的吞吐量。
为了评估TILE-Gx72上不同自旋锁的可伸缩性,我们在FMP中实现了退避自旋锁,在Linux中实现了TAS和TATAS自旋锁。我们还实现了K42自旋锁,它是可伸缩MCS自旋锁的一个变体,具有兼容的api,用于比较可伸缩和不可伸缩实现之间的差异。
使用微基准测试了FMP和Linux上不同自旋锁的吞吐量,结果如下。
当核数量较少(小于6)时,TAS和TATAS自旋锁的吞吐量确实比其他自旋锁的吞吐量要好,但会迅速下降。退避自旋锁的吞吐量下降速度比TAS和TATAS慢得多,并且在使用所有核时仍然可以提供相对可接受的性能。K42自旋锁可以在核心数量增加时保持良好的可扩展性,但在少于3个核竞争共享资源时性能最差。
结论
结果表明,尽管传统的RTOS不是为执行高性能应用程序而设计的,但在许多情况下,它可以提供比Linux更好的可扩展性。因此,由于之前的研究表明Linux可以在具有数十个核心的多个核心处理器上很好地扩展,因此我们相信传统RTOS(如TOPPERS/FMP)也可以是嵌入式多核心处理器的良好选择。