Xenomai——实现一个GNU/Linux上的RTOS的仿真框架

来源:互联网 发布:sql truncate 用法 编辑:程序博客网 时间:2024/05/10 00:43

Copyright © 2002 Philippe Gerum

【摘要】

  一般来说,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/ ) 。


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 SystemsProgramming 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 动态的管理,这仅仅限制于实际系统内存量。

0 0