Xenomai-实现
一个GNU/Linux上的RTOS的仿真框架
Philippe Gerum
第一版
Copyright ©2004
Copyright © 2002 Philippe Gerum
在遵循由自由软件基金会发布的1.2或更高版本的GNU Free Documentation License的前提下授予许可复制,分发和/或修改本文件。无固定段落,封面文字和封底文字。许可的一个副本发布在gnu.org上:“GNU Free Documentation License”[http://www.gnu.org/licenses/fdl.html]。
2004年4月
摘要
一般来说,Xenomai技术起初旨在帮助依靠传统RTOS应用程序设计者尽可能顺利移动到一个基于GNU/ Linux的执行环境,而不必完全重写他们的应用程序。
本文讨论了提出此框架的动机,传统的RTOS指导该技术的一般意见和它实现的一些深层次的细节。
Xenomai项目开始于2001年的8月。为了为GNU/Linux生产一个工业级的实时自由软件平台,于2003与RTAI(http://www.gna.org/projects/rtai/)项目合并,名为RTAI/fusion,运行在Xenomai的抽象的RTOS内核之上。最终,经过RTAI/fusion的努力,RTAI/fusion在2005作为xenomai项目独立出RTAI(http://www.gna.org/
projects/xenomai/)。
Linux是Linus Torvalds的一个注册商标。本文中提到的其他商标是其各自所有者的财产。
目录
1. 白皮书
1.1. 引言
1.2. 将基于传统RTOS的应用移植到GNU/Linux
1.3. 一个通用的仿真框架
1.4. 核描述
1. 白皮书
1.1. 引言
从传统RTOS到GNU/Linux的一个简单的迁移路径,可以有利于更广泛地接受后者作为一个实时的嵌入式平台。提供的模拟器来模拟传统的RTOS的API是自由软件社区采取填补非常分散的传统RTOS的世界和GNU / Linux世界之间的差距的倡议之一,为了使依赖传统RTOS的应用程序设计者尽可能平滑的过渡到GNU/Linux平台。
目前缺乏共同的软件框架来开发这些模拟器,而传统的RTOS的行为之间的相似性是显而易见的。
Xenomai技术旨在填补这一空白,利用这些相似之处提供一致的体系结构中立和通用仿真层。同时,还致力于提供一套建立在这层的顶部传统的RTOS模拟器的增长集。
Xenomai依赖于发现在许多嵌入式传统的RTOS之间的共同特征和行为,特别是从线程调度和同步的立场来看。这些相似之处被利用来实现一个提供了一组通用服务的核。这些服务被分组在一个高层次的接口中,能被依次使用来实现实时应用程序编程接口的仿真模块。这些接口模仿了相应实时内核的API。
在仿真领域类似的方法也被用于 CarbonKernel(http://savannah.gnu.org/projects/carbonkernel)的项目[1],其中RTOS仿真模型的建立在基于事件驱动技术的一个通用的虚拟RTOS之上。
1.2. 移植基于传统RTOS的应用到GNU/Linux
使用GNU/Linux作为具有实时能力的嵌入式系统的想法并不新颖。读者可以参阅Jerry Epplin 在97年10月份的文章issue of Embedded Systems Programming for a discussion about GNU/Linux potential in the embedded field。
在整个文档中,我们将会使用表达式
source RTOS来指明要移植的应用程序的源传统实时操作系统,并用
target OS来指明应用程序要被移植的GNU/Linux或其他免费操的目的操作作系统。
1.2.1. 有限的高层代码修改
当尝试将硬实时应用移植到另外一种体系结构时,保持它最初的设计和实现显然是最大的利益。可靠性和性能需要一个漫长复杂的工业过程之后才能获得,而这个过程往往不是我们所希望的。因此,最好的情况是在源和目的RTOS的编程接口尽最可能的等价,尽可能的将语法和语义考虑在内。
这方面的一个例证可以取自通过互斥服务的优先级协议的支持。这些服务允许并发的线程保护他们自己不受竞争条件的影响,竞争条件将会导致进入临界区代码。这个讨论的目的不是去争论是否依赖于优先级继承来解决优先级翻转问题对于一个实时应用来说是一个重大的设计缺陷或者是一个必不可少的安全带,而只是来强调在任何情况下,如果这种特性被应用在源RTOS,而没有应用在目标RTOS上,那么对于这个应用资源管理策略必须被重新评估,因为优先级翻转的危险依然存在。
1.2.2. RTOS行为兼容性
在过去的几年里,主要的嵌入式RTOS,例如VRTX、VxWorks、pSOS+和其它几个已经实现了一个实时内核的行为,这些行为已经成了事实上的标准,特别是线程调度、线程间同步和异步事件管理。为了说明这一点,让我们来谈谈一个在中断服务管理里特定的问题。
这样的RTOS的一个众所周知的行为是锁住重新调度进程直到外部中断服务例程(或ISR)——第一次调用可能是嵌套中断——退出,在此之后全局调度才会最终开始。这种方式,一个中断服务例程可以总是假定没有同步线程活动直到它运行结束。而且所有的改变都会影响线程的调用顺序,由于任何数量的嵌套ISRs(例如:信号同步对象,一个或多个线程将会被阻塞)的行为都会被认为是一次性的和连接的,而不是分离的。
例如,如果一个挂起的线程被ISR第一个恢复,然而被同一个ISR的别的部分强行挂起,结果将会是这个线程不会运行,并且在ISR退出后仍然处于被挂起状态。另一方面,如果RTOS看到ISRs作为分特异性代码,能被线程抢占,先前考虑的线程被恢复之后将会立即得到执行的机会,知道它重新被挂起。显然,各自的情况下,将不是相同的。
1.2.3. 实时约束的重新评估
将GNU/Linux做成一个硬实时系统目前通过使用一个辅助内核(co-kernel)的方法实现。这个辅助内核控制硬件中断管理,并且允许与宿主GNU/Linux系统无缝运行实时任务。‘常规’的Linux内核被视为一个低优先级、小实时执行的背景。RTLinux(http://www.rtlinux.org)项目是这种技术路线的代表。然而,这种方法有一个主要的缺陷当谈到从一个国外的软件平台移植复杂的应用时:由于实施任务的运行在Linux内核的控制之外,当移植这些应用时GNU/linux编程模型不会被保留。结果将会是增加了重新设计和调试这些移植的代码的复杂性。
在某些情况下,选择一个传统的RTOS来运行一个嵌入式应用已经被目标硬件的内存限制最初决定,而不是应用自己的实时约束。由于嵌入式设备往往表现出不断增加的内存和处理能力,当考虑到移植GNU/Linux到一个新目标硬件上时,重新评估实时需要似乎是明智的。因此,可以选择最好的底层软件架构。在这点上,需要考虑以下条件:
- 确定性和重要性
什么是最坏的情况下中断和调度所需的延迟?
错过了最后期限是否导致灾难性的失败?
- 编程模型
什么是整体应用程序的复杂性,假设越高的复杂性,对有力的调试帮助和监视工具会有越大的需求。
- 是否有必要需要低层次的硬件控制
是否有实时活动耦合到非实时的服务中,例如GUI和数据库,需要与非实时的世界复杂的通信?
1.2.4. 现有的一些解决方案
为了得到无论是硬实时或者软实时的支持,存在很多基于GNU/Linux解决方案。本文档的目的不是相近的描述它们。我们仅仅介绍一个基于自由软件解决方案的两折方法,这个方法很可能适用于很多移植任务,依赖于应用的实际的实时约束。
1.2.4.1. 用实时GNU/Linux扩展部分重写
使用RTAI使GNU/Linux实时。严格来说Linux/RTAI不是一个实时系统,而是一个实时Linux内核扩展,它允许出运行主GNU/Linux系统外无缝运行实时任务。RTAI辅助内核与Linux内核通过使用Adeos(http://www.adeos.org)虚拟层共享硬件中断和面向系统的事件,例如陷阱和缺陷,它反过来确保RTAI的低中断延迟。原始的RTAI API为应用提供了丰富有用的服务,包括计数信号量、POSIX 1003.1-1996 设施,例如pthreads,mutexes和condition variables,还增加了远程过程调用设施,信箱和精确计数器。大多数服务从内核模块和用户空间程序是可对称获得的。
RTAI 2.x和3.x提供了一种方法在用户空间上下文中执行硬实时任务(仅在x86下),但是仍然在Linux内核的控制之外,运行“用户空间的内存模块”是对它最好的描述。这种特点,名为LXRT,是从传统RTOS简单移植路径的一大步,由于编程错误发生在实时任务不危害了整体的GNU/Linux系统的健全,只是多了几微妙延迟的代价。
Ad hoc 服务仿真。 第一种方法包括模拟由应用程序需要每个实时设施,使用了RTAI服务的组合实现。一个ad hoc 包装接口必须被写成支持需要的函数调用。包装方法的好处在于对原始代码的修改有限。然而,某些RTAI行为可能不符合原操作系统。对于同样的原因,仿真的和原生RTAI的服务之间在某些情况下可能会产生冲突。
完全移植到RTAI。第二种方法包括在原生RTAI API上完全一致应用。在这种情况下,RTAI设施全局替代原RTOS的设施。这种解决方案以可能大规模改写应用代码的代价提高了一致性,由于某些基础行为的差别,这些差别可能会存在于传统的RTOS和原生的RTAI接口之间。
1.2.4.2. 无约束的用户空间仿真
几个传统的RTOS模拟器存在在自由软件世界中。通常设计在GNU/Linux POSIX 1003.1-1996层上,并且允许在用户空间执行上下文中模仿源RTOS API,并且在Linux内核的控制之下。
曾经在这方面最proeminent努力的之一是 Legacy2linux项目。这个项目由Montavista Software赞助,目的是提供["为多种传统RTOS提供一系列Linux-resident 仿真器"]就像Xenomai一样,[这些仿真器被设计成去简化传统RTOS 代码到嵌入式Linux环境的移植。]这个项目提供了两个版本的仿真器,分别模仿WindRiver的pSOS+和VxWorks操作系统的APIs。然而这个项目由于缺少维护和奉献已经停滞了。
这种方法的优点是保持开发进程在GNU/Linux用户空间环境,而不是进入一个“有敌意的”内核/管理员模式上下文中。这种方式有一种现成的丰富的工具,例如对于应用开发者在这种上下文环境中立即可用的调试器、代码分析器和可用的监视器。此外,标准GNU/Linux编程模型被保留,允许应用程序使用存在于用户空间的所有施舍(例如:完全POSIX支持,包括进程间通信)。最后但并非最不重要,在这个上下文中的编程错误不会危害全局GNU/Linux系统的稳定性,不会发生像发生在硬实时内核那样的一个错误可能会对运行中的Linux内核造成严重的损坏。
然而,在使用这些仿真器过程中我们至少发现了三个问题,取决于应用的限制:
- 首先,他们提供的便于从源RTOS移植的仿真API通常是不完整的。换句话说,只有有限的语法兼容是可用的。
- 其次,源RTOS的确切行为对于所有的功能域是不可重现的。换句话说,无法保证语义的兼容性。
- 这些仿真器没有共享任何实现基本实时行为的通用代码库,即便是pSOS+和VxWorks共享了他们的大部分。结果是导致了实现的冗余。
- 最后,最后,即使整合了最新的Linux 2.6功能,如fine-grain内核抢占和低延迟的努力,这些模拟器仍就无法提供确定性的实时性能。
1.3. 一个通用的仿真框架
1.3.1. 通用的传统RTOS的行为
为了建立一个通用和灵活的框架来模拟传统的RTOS,我么选择把精力集中在他们都表现出的一组共同的行为上。特定RTOS特点的一组有限集合,虽然不常见,但是相比实现在仿真层可以更高效的实现在nucleus层,也被保留了。基本的行为的选择涵盖了四个不同的领域。
1.3.1.1. 多线程
多线程为应用程序控制、对多个反应和离散外部事件提供了一个基本的机制。nucleus提供了基本的多线程环境。
线程状态。nucleus必须维护系统中每一个线程的当前状态。从一个到另外一个状态的转换可能说作为RTOS仿真器调用特定nucleus服务的结果。nucleus定义的线程的基本状态是:
- DORMANT 和 SUSPENDED状态是累积的,意味着新创建的线程再被从DORMANT状态恢复后将会保持一个挂起状态。
- PENDING 和 SUSPENDED状态也是累积的,意味着一个线程在等待一个同步资源的时候能被另外一个线程或服务例程强行挂起(例如:信号量、消息队列)。在这种情况下,资源会被分发给它,但是这个线程会持续被挂起状态直到适合的nucleus服务恢复它。
- PENDING 和 DELAYED状态可以结合来表达对一个资源的定时等待。在这种情况下,时间线程的阻塞势必会受到看门狗的制约。
调度策略。默认的情况下线程的调度依据一个固定的优先级使用可抢占调度算法。对一组具有相同优先级的线程有一个轮询调度的支持,允许它们在一个给定的时间片内交替运行。而且每个进行轮询调度的线程都会被给与私人的时间片数量。
优先级管理。使用升高或者降低线程优先级的策略是可能的,这取决于初始配置。换句话说,数字上的高优先级根据配置选则可以表示高的或者低的调度策略。此特点受在传统RTOS中存在的这两种可能排序的启发。例如:VxWorks、VRTX、TheadX和Chorus O/S使用相反的优先级管理方案,在它们中值越大优先级越低。PSOS+而是使用相反的顺序,值越大优先级越高。
线程运行。在任何时间,调度器会选择在当前可运行的线程(即,不是被任何延时或资源等待阻塞的线程)中选择已经准备好运行最长时间的优先级最高的线程运行。
抢占。当被更高优先级的线程抢占后,正在运行的线程会被放在就绪线程队列的最前面来等待处理器资源,并假设它没有被任何形式的挂起或阻塞。因此当没有其他更高优先级的活动(即,一个具有更高优先级的线程,或者是中断服务例程)被选中运行时它有有望越快越好的获得处理器资源。
手动时间片轮转。作为尝试去恢复一个已经可运行的线程或者正在运行的线程自己,这个线程将会被移动到就绪线程队列的同组优先级的末尾。这个操作功能上等同于手动优先级调度。
即使他们不像跟在传统的RTOS那样普遍,以下功能为了提高效率在一些模拟器也保留了:
优先级翻转。为了提供对阻止使用线程间同步服务时优先级翻转的支持,优先级继承协议将会被支持。
信号。一种对线程和正在运行的异步服务例程发送信号来处理他们的支持可用。异步服务例程代表了它下次从nucleus层执行返回的运行信号线程上下文,一旦一个或多个信号被挂起。
分离等待。一个线程能用用分离的方式等待个资源。当至少有一个等待资源可用时nucleus会解锁该线程。
1.3.1.2. 线程同步
传统的RTOS提供了一个大范围的线程间通信设施涉及线程同步,例如:信号量、消息队列、事件标志和信箱。仔细观察他们,我们能定义一个基本机制的特点,这些特点在反过来创建这些基本设施的时候会用的到。
阻塞模型。线程同步设施为队列通过优先级或FIFO队列方式阻塞提供的一种方法。多个线程必须能在单个资源上阻塞。
优先级继承协议。为了阻止优先级翻转问题,线程同步设施与线程调度器实现了一个优先级继承协议。这个实现允许支持作为优先级继承协议衍生的优先级上限协议。
有限等待。线程同步设施使用看门狗提供了一种限制线程等待给定资源的方法。
强制释放。释放一个其他线程都等待的资源是合法的。这个操作可以原子的恢复所有的等待者。
1.3.1.3. 中断管理
由于中断处理是在RTOS设计中最不好的定义区域之一,nucleus着重提供一种简单的机制,对于特定的实现拥有足够的钩子建立并依据仿真的RTOS风格。
递归。为了安全的支持中断嵌套,中断管理代码须是可重入的。
原子性。中断与称作SIR的专用的服务例程联系在一起。为了使这些例程不被线程的运行抢占,重调度器将会被锁定知道外部ISR退出(即,防止嵌套中断)。
优先级。ISRs始终被视为优先级高于线程运行。中断优先级由底层硬件处理。
1.3.1.4. 时间管理
传统的RTOS通常以滴答为单元表示时间。它们是特定时钟的时间单位并且通常是硬件定时器中断的周期,或者倍数。由于需要支持周期的和非周期的时间源,nucleus能根据当前定时器操作模式从周期的jiffies到时间戳计数器值透明转换。
软定时器支持。nucleus提供了一个看门狗设备来管理时限操作。
决定和相对时钟。nucleus保存了一个全局时钟值,这个值可以被RTOS仿真器设置作为系统定义的时代。
有些RTOS像pSOS+也提供了支持基于日期的时机,但是滴答转换成传统的时间和日期单元不是一种常见的需要,这种转换必须有RTOS仿真器自己负责。
1.3.2. 一个体系结构中立的抽象层
挑选出传统RTOS共享的基本行为之后,我们已经在nucleus层实现了他们并导出了依稀服务类。这些通用的服务将会作为一个基础层,为了开发每个仿真RTOS API,并且会依据他们各自的风格和语义。
为了这层体系结构的中立,硬件控制和实施能力的必要支持将会通过一个简单的标准化接口从底层主机的软件体系结构中获得。因此,移植nucleus到一个新的实时体系结构中将会仅仅包含目标平台底层接口的实现。
1.3.3. 实时功能
主机软件体系结构被期待为RTOS的抽象层提供首要的实时功能。从根本上说,主机的实时层应该最少处理一下任务:
- 根据要求启动/停止派遣外部中断到专门的处理程序;
- 提供一种方法来屏蔽和不屏蔽中断;
- 提供一种方法用他们最简单的格式来创建新的线程控制。
- 对定应用在定时器管理的周期性和非周期性的中断源的支持。
- 提供对非分页内存分配的支持。
1.3.4. 好处
Xenomai的目标为帮助依赖传统RTOS的应用程序设计者尽可能顺利的向基于GNU/Linux执行环境的转换,而不需要完全重写他们的应用。除了将GNU/Linux作为嵌入式系统使用的优点外,从上述的方法得到的预期收益主要是减少了设计新RTOS仿真的复杂性。体系结构中立的抽象层提供了开发准确描述传统RTOS API的基础,见笑了反复实现其基本的实时行为的负担。由于抽象层也利于代码共享和彼此有利,我们可以预期RTOS仿真利用他们的代码稳定性和可靠性方面。
1.4. Nucleus 描述
RTOS仿真是通过pod抽象连接到nucleus的软件模块。pod负责关键的家政劳动和线程的实时调度。
1.4.1. 多线程支持
nucleus提供了线程对象(xnthread)和pod(xnpod)的抽象,他们展示出了以下特点:
- 线程调度依据一个32位整形的优先级,用抢占的调度算法。优先级顺序依据pod的配置可以是增大或减小的。
- 一个线程可以有以下状态:等待初始化、被强制挂起、等待资源阻塞、延时一定的时间、就绪或运行中。
- 每个线程看门狗可以被界定定时等待一个资源
- 优先级继承协议被支持去阻止线程优先级翻转,当它探测到同步对象的时候。
- 一组拥有相同基本优先级的线程会经历一个轮询调度,他们中的每一个会被给予一个私人的时间数。
- 对给线程发送信号和运行一部服务例程(ASR)来处理他们的支持是内建的。
- 对于任何线程FPU的支持可以是在创建时选则启用或者禁用。
- 每个线程可以进入分离等待多个资源。
1.4.2. 基本同步支持
nucleus提供了一个同步对象抽象(snsynch)目标是实现RTOS资源的通用行为,包括以下特点:
- 支持优先级继承协议,为了防止优先级翻转问题。这个实现与调度器代码实现共享。
- 对于等待着唤醒支持时间限制的等待和强制删除。
1.4.3. 定时器和时钟管理
nucleus需要时间源来为高层的接口提供关于时间的服务。定时器硬件需要被设置以便用户定义的例程根据给定的频率被调用。架构上提供了一个可编程的oneshot时间源,系统定时器可以操作周期性或非周期性的模式。使用非周期性模式任然允许在其上运行周期性nucleus定时器:定时器管理器使用恰当的间隔值之后底层硬件将会被重新编程。
每个传入的时钟滴答将被传给定时器管理器,它反过来触发应经过时定时器的处理程序。调度器本身使用每个线程的看门狗来唤醒一个经历有限时间等待的线程,此线程在等待一个可用的资源或者被延迟了。
对最坏时间界情况下的启动、停止和保持定时器采取特别关注。定时器设施基于定时器轮转算法,由Adam M. Costello 和 George Varghese描述,并在NetBSD系统中实现。
1.4.4. 基本的内存分配
Xenomai的nucleus提供了有实时保障的动态的内存分配的支持,基于McKusick 和 Karels 对于通用目的内存分配器的提议。任意数量的对内存都可以由Xenomai动态的管理,这仅仅限制于实际系统内存量。