文章目录

1. 简介

1.1 实时OS

现阶段的RTOS分成两个阵营:

  • 非Linux阵营:VxWorks,RTEMS
  • Linux阵营 :RT-linux,Preempt-rt,WindRiver Linux,RTAI,Xenomai。

下面重点介绍Linux阵营。

  • RT-linux:应该是第一批实时性linux,可以提供硬实时,有两个版本,免费版和收费版,收费版最初由 FSMLabs(Finite State Machine Labs)公司开发,在2007年卖给了 WindRiver。之后 WindRiver 将其作为自己的产品 WindRiver Linux 的一个实时核心(real-time core)象征性的维护了一段时间,与2011年8月彻底停止更新和维护。RT-linux 的性能应该很好,WindRiver 应该是出于竞争的目的对其进行了收购,现在 RT-linux 已经不再更新,因此对于我们也只能当做了解一段历史了。

  • WindRiver Linux:WindRiver 为 Linux 世界提供的一个发行版,具备软实时的能力,应该吸收了一部分 RT-Linux 的核心技术,感觉是 WindRiver 为了延长其产品线,提高行业竞争力和覆盖面而开发的系统,还没进行深入分析,毕竟是软实时。

  • Preempt-rt:Ingo Molnar 引领的 Linux kernel 实时 patch,可以改善通用 Linux 的实时性,也是本项目前期工作的重点。

  • RTAI:(Real-Time Application Interface)项目应该继承自 RT-linux,后期吸收了 Xenomai 的一部分思想,专注实时性能,应用广泛。

  • Xenomai:2001 年 8 月由 Philippe Gerum 发起,其思想是来源于 Karim 的 ADEOS(Adoptive Domain Environment for Operating System)。发布后即被 RTAI 采用,并一度合并为 RTAI/Fusion。后于2005年独立。Xenomai 的实时性能比RTAI略差,因为其完全由 ADEOS 控制中断,而 RTAI 是由其内核对中断进行了截断,非实时的中断才交给 ADEOS,这就减少了一部分实时开销。

双核(如RTLinux,RTAI,Xenomai)实时方案。运作一个real-time核心,然后将修改过的GNU / Linux核心程序码视为该实时核心的空闲任务。

常用的双内核法实时补丁有 RTLinux/GPLRTAIXenomai,其中RTLinux/GPL只允许以内核模块的形式提供实时应用;而RTAI和Xenomai支持在具有MMU保护的用户空间中执行实时 程序。

1.2 Adeos

Adeos的全称是Adaptive Domain Environment for Operating System,它的目标是为操作系统提供了一个灵活的、可扩展的自适应环境,在这个环境下,多个相同或不同的操作系统可以共存,共享硬件资源。

目前,主要存在两类方法使多个操作系统运行在同一个系统上:

  • 第一类方法是模拟,例如,VMWare,Plex86和VirtualPC等;它们都是在已有的操作系统上提供一个虚拟的硬件环境,在这个虚拟硬件环境下可以运行另外操作系统。这样,用户就可以充分的利用两个或多个系统所提供的功能和软件;但这种方法最大的缺点就是会极大的降低系统的性能,因为它包含三个软件层次:主机操作系统(Host Operating System) -> 虚拟硬件环境 -> 客户操作系统(Guest Operating System)。
  • 第二类方法,是在硬件上实现一个所谓的超微内核(nano-kernel),通过这个超微内核实现硬件的共享,然后再在这个内核上构建实用的操作系统,例如,SPACE,Cache kernel和Exokernel等。

但这些方法都没有考虑当前已经存在的操作系统和用户。

Adeos是在已有的操作系统下插入一个软件层,通过向上层多个操作系统提供某些原语和机制而实现硬件共享。但是Adeos并不对硬件的使用强加任何的限制,上层的操作系统仍然可以自由的操作硬件,而不会因为Adeos的存在而有任何的约束(实际上,上层的操作系统可以完全不知道有Adeos的存在)。
Adeos除了可以实现操作系统对系统资源的共享之外,还可以用于新的操作系统的开发、操作系统内核的调试、跟踪等。
目前,Adeos是基于Linux内核实现的,主要的应用是在Linux的实时化方面,使基于Linux的系统能满足硬实时的要求(Linux+RTAI)。

Adeos实现的功能主要包括:

  • 中断管道机制(I—Pipe)、
  • 域管理模块和
  • 域调度模块功能。

域 (Domain)

为此,Adeos允许多个实体(称为域)同时存在于同一台机器上。这些域名不一定相互看见,但所有这些域名都看到了Adeos。域很可能是一个完整的操作系统,但没有假设关于域中的内容的复杂性。但是,根据系统范围内的优先级,所有域都可能竞争处理外部事件(例如中断)或内部事件(例如陷阱,异常)。

在基于Adeos的系统中,每个操作系统都在独立的内运行(但不一定所有的域内实现的都是操作系统,也可以是完成其它功能的软件实体),每个可以有独立的地址空间和类似于进程、虚拟内存等的软件抽象层,而且这些资源也可以由不同的域共享。在基于Adeos的系统中,存在着四种类型的交互,如下图所示:
在这里插入图片描述

  • A类交互是各个域对硬件的直接操作,这些操作包括访存和和对硬件的设置等,在这种情况下,就和Adeos不存在一样;
  • B类交互是双向的,一方面Adeos接收硬件产生的中断和异常,另一方面,Adeos也直接控制硬件;
  • C类交互指当Adeos接收到硬件中断后,会执行相应域的中断服务程序;
  • D类交互指当域内的操作系统知道有Adeos存在的时候,它可以主动向Adeos请求某些服务,例如,请求共享其它域中的资源、请求授权域优先级等。通过D类交互,可以实现各个域之间的通讯。

中断管道(Interrupt Pipe)

对于一个计算机系统来说,系统的运行是由内部和外部的中断和异常所触发的,例如系统时钟中断对操作系统来说就是最重要的,操作系统没有了系统时钟中断,就像人没有了心跳一样,所以说,如果想要控制操作系统的运行,最直接的方法就是接管操作系统的中断处理机制。所以,Adeos的主要工作就是管理硬件的中断,根据域的优先级依次执行相应域的中断服务程序,从而驱动域内的系统运行;同时,Adeos还提供域之间的通信机制、实现域的调度等。为了实现对中断的管理和域之间的优先级控制,Adeos使用了中断管道(Interrupt Pipe)的概念,如下图所示:
在这里插入图片描述

Adeos通过中断管道在不同的域之间传播中断,而且提供了相应的机制可以让域改变自己在中断管道中的优先级,在图中,各个域的优先级为:域1>域2>……>域N>Idle域。

通常,在操作系统中,对中断的处理方式有两种:允许中断和禁止中断;但在基于Adeos的系统中,由于存在着中断管道,域内的操作系统对中断的处理方式还有另外两种:抛弃中断和终止中断。

  • 如果某个域允许中断,中断产生后,Adeos会调用相应域的中断处理程序,这和不存在Adeos的情况是类似的,只不过在这种情况下,中断服务程序由Adeos负责调用;
  • 如果某个域禁止中断(实际上并没有真正禁止硬件中断,而只是设置了一个软件标志),当硬件中断沿沿着中断管道进一步向下传播。
  • 如果某个域抛弃某个硬件中断,当中断传播到这个域的时候,Adeos不做任何的处理,直接将这个中断沿着中断管道向后传播。
  • 如果某个域终止某个中断,当中断传播到这个域的时候,Adeos根据这个域的设置处理完这个中断之后,不再将这个中断沿着中断管道向后传播,也就是说,后面低优先级的域将不知道有这个硬件中断的产生。

所以,Adeos就是通过控制系统的中断来实现对各个域内操作系统的控制。从下图可以对基于Adeos的系统的运行模型有一个整体的概念;其中,D1、D2分别代表两个域,且优先级为D1>D2,为了使描述更加清晰明了,对系统作如下的假设:
系统有两个域D1和D2,两个域完全一样,除了优先级D1>D2,且两个域都允许中断;
整个系统只有两个硬件中断INT1和INT2;
每个域有两个中断服务程序ISR1和ISR2,分别对应于INT1和INT2;
每个域有两个任务TASK1和TASK2(不包括IDLE任务),分别由ISR1和ISR2触发运行;且TASK1的优先级高于TASK2(只有当TASK1任务完成后,TASK2才能开始运行);
在这里插入图片描述
从上图可以看出:

  • 在T1时刻,Adeos接收到了硬件的中断信号,然后就开始遍历中断管道,找到最高优先级的域D1,然后执行域D1的中断服务程序ISR1,ISR1执行完后,就切换到域D1,D1内的任务TASK1开始运行;
  • TASK1运行完成后,域D1就被挂起(Suspended),Adeos然后执行D2的中断服务程序ISR1,ISR1执行完后,就切换到域D2,开始D2内TASK1的运行;
  • 当D2的TASK1运行到T2时刻时,硬件产生了中断信号INT2,域D2被中断,Adeos接收到INT2后,又再一次开始从头遍历中断管道,找到了最高优先级的域D1,然后执行D1的中断服务程序ISR2,ISR2执行完后,就切换到域D1,开始D1内任务TASK2的运行;
  • TASK2运行完成后,域D1被挂起,Adeos然后执行D2的中断服务程序ISR2,ISR2执行完后,就切换到域D2,并开始域D2内被中断的任务TASK1和新触发的任务TASK2的执行;
  • 当域D2的任务都执行完成后,域D2被挂起,系统进入IDLE状态。

事件管道(event pipeline)

必须牢记的基本Adeos结构是要求事件控制客户域链。域是基于内核的软件组件,可以要求Adeos层通知:

  • 每个传入的外部中断或自动生成的虚拟中断;
  • Linux应用程序发出的每个系统调用,
  • 内核代码触发的其他系统事件(例如Linux任务切换,信号通知,Linux任务退出等)

Adeos确保根据系统中各自的优先级以有序的方式将事件分派给各个客户域,因此可以提供这些事件的及时和可预测的传递。这是通过为每个域分配静态优先级来实现的。此优先级值严格定义事件到域的传递顺序。所有活动域根据其各自的优先级排队,形成Adeos用于使事件从最高优先级域到最低优先级域流动的管道抽象。将传入事件(包括中断)推送到管道的头部(即,到最高优先级域)并向下进行到其尾部(即,到最低优先级域)。下图给出了一些基于Adeos的系统的一般视图,
在这里插入图片描述

在上图中,Linux内核相对于流水线订单的位置可能是任何位置; 这就是说,Linux内核仍然有一个特殊的角色,因为它代表根域,因为所有其他域都需要Linux来安装它们,通常是通过加载体现它们的内核模块。

乐观的中断保护

为了以优先级的方式分发中断,同时仍允许域创建无中断部分,Adeos实现了Stodolsky, Chen, 和 Bershad描述的所谓的乐观的中断保护模型。在这篇的论文中。[http://citeseer.nj.nec.com/stodolsky93fast.html]
任何给定的域占用pipeline的阶段都能被推迟,这意味着下一个到达的中断将不会被分发到域的处理者并将会被阻止以同样的举动流到最低优先级域。
当一个阶段被推迟,暂停的中断累积在域中断日志中,最终在这个阶段不再被推迟时被一个名为同步的内部操作处理。域使用这种特点来保护他们自己的临界区不被他们自己的中断处理有害的抢占。
然而,多亏Adeos带来的中断控制虚拟化,一个更高优先级的域仍能接收中断并且最终抢占任何低优先级域。实际上,这意味着, 即使一个基于Adeos的Linux内核经常推迟它自己的阶段来处理临界区操作,一个提前运行的实时系统在pipeline中仍能在任何时候够接收中断,不会产生任何延迟。
当一个域处理完了它所接收到的待处理的中断,它会调用一个特殊的Adeos服务让出CPU到pipeline的下一个域,所以后者可以依次处理唤醒它的待处理的事件并且这个周期会持续到管线的最低优先级域。
下图说明了运行在多CPU上的多个域怎样通过Adeos pipeline抽象共享传入的中断。当然,当一个阶段被推迟,必须正确的标记待解决中断:这是通过一个每域,每CPU中断日志实现的,见下面的解释:
在这里插入图片描述

系统事件传播

中断不是唯一一种可以流经管线抽象的事件;Linux内核自己触发的内部事件或者它的应用程序产生的所谓的系统事件。从根本上说,系统事件是陷阱、异常或某些Linux内核的动作的同步通知,并通过管线通知任何感兴趣的部分。

由于这些事件的本质是同步的,没有办法通过推迟操作推迟他们的唤醒,仅仅是因为你不能延迟他们的处理。这样设计决定的依据来源于这样一个事 实:代码触发一个系统事件可能仅仅是没有响应处理程序的干预不能继续处理:例如,缺页处理程序在内存寻址异常后应该立即执行,延迟它没有任何意义。换句话 说,在给定的域上的延迟/非延迟操作仅仅关心中断,不管是真实的还是虚拟的。

基于Linux的实现

考虑到从硬件层开始构建一个操作系统的难度,Adeos并没有并没有从零开始构建一个硬件抽象层;目前,Adeos是基于Linux内核实现的,这样的话,就可以将系统的启动和初始化工作都由Linux来完成,在系统完成初始化后,再进行Adeos的初始化工作(包括接管Linux的中断管理机制),Adeos功能既可以直接编译进内核,也可以作为一个内核模块在系统运行时动态加载,就和内核的驱动程序模块一样。
在这种实现方法下,Linux作为Adeos的一个特殊的域存在,我们称之为根域(Root Domain)。Adeos的很多功能都是依靠根域(也就是Linux内核)来实现的,例如,动态注册其它的域模块是通过Linux的动态模块加载功能实现的,为其它域的任务分配任务堆栈是通过Linux内存分配接口实现的等。根域的初始化是在Adeos的初始化过程中完成的;根域对于Adeos来说,有一点类似于Linux初始化过程中创建的INIT进程;

Adeos在Linux配置中增加了三个配置开关来配置Adeos的代码:CONFIG_ADEOS_CORE,CONFIG_ADEOS和 CONFIG_ADEOS_MODULE。

  • 如果定义了CONFIG_ADEOS_CORE,Adeos的核心支持就被编译进了Linux内核,不论 Adeos功能最终编译进内核还是编译成可动态加载的模块,这个编译选项都必须被定义;
  • 如果定义了CONFIG_ADEOS,则也隐含着对 CONFIG_ADEOS_CORE的定义,Adeos功能就被编译进了内核,那么,从Linux启动以后,Adeos功能就被使能了;
  • 如果定义了 CONFIG_ADEOS_MODULE,则也隐含着对CONFIG_ADEOS_CORE的定义,Adeos功能被编译成可动态加载的模块,只有当这个 模块被加载后,Adeos的功能才会起作用。Adeos对Linux源代码树的修改涉及到30多个文件(包括新增加的文件),如下图所示:
    在这里插入图片描述

其中的Makefie和Config.in文件,是为Adeos代码添加内核编译选项和内核配置选项;
adeos目录下的generic.c包含了与平台无关的通用的Adeos代码,而armv.c则包含了与ARM平台相关的通用于ARM平台的Adeos代码;
在arch/arm/kernel目录下,adeos.c中包含了Adeos关于中断处理的代码;armksyms.c中增加了代码,导出了ARM体系相 关的Adeos接口; entry-armv.S和entry-common.S中增加了截获Linux中断和系统事件的代码,域的切换代码也在此处实现; 同时,修改了irq.c以适应Adeos的中断处理机制;在process.c中修改了Linux的idle进程,当Linux进入idle状态时,将通 知Adeos;time.c中增加了Adeos中修改定时器频率的接口;
Documentation/adeos.txt对Adeos进行了简单的介绍;
在init/main.c中增加了对Adeos进行初始化的代码;
kernel/adeos.c中包含的是Adeos对根域也就是Linux操作的代码;对kernel目录下的其它文件的修改,主要是为了增加Adeos对Linux系统事件进行捕获所需的代码;

1.3 Adeos为Xenomai提供的服务

Xenomai需要提供什么样的基本保证才能提供实时服务?答案很简单明了:

  • 在Linux内核有机会注意到它们之前,必须首先允许它处理所有传入的中断,并且必须能够立即处理它们,无论当前从Linux内核到使用CPU中断屏蔽将它们锁定。
  • 它还必须确保始终对其线程强制执行适当的优先级管理,而不管它们当前的执行域。

无论Linux正在进行什么样的活动,这些保证都能让Xenomai在最低的微秒级范围内实现可预测的中断延迟,这种活动曾与Xenomai的Linux任务的快速协同调度技术(即影子线程)相结合,提供了实时线程的确定性调度延迟。
在这里插入图片描述
您会注意到Adeos接口直接暴露于作为Xenomai核心基础的硬件抽象层。因此,大多数对Adeos服务的请求都是从HAL发出的,该实现可以在相关arch//hal
目录中找到,通用位可用于arch/generic/hal。查看后者是理解Xenomai如何将Adeos用于其自身目的的最佳方法。

Xenomai的主要和次要域名

Xenomai允许运行实时线程在严格内核空间或者在Linux线程地址空间。在文章剩下的部分,我们把后者称作Xenomai线程,不会与 常规Linux任务产生混淆(即使他们属于 SCHED_FIFO 类)。Xenomai管理的所有线程可以从实时nucleus中获知。

对用户空间中真正实时支持的出现之前,实时应用程序只运行到内核模块中,支持实时线程排他的运行在内核空间仅仅是一个单核时代的一个回忆。Xenomai中保留此功能主要是为了支持遗留应用程序,此处不再讨论。

更有趣的是,Xenomai在Linux方面采用了共生方法; 例如,这就是它与RTAI / LXRT实现的不同之处。为此,Xenomai线程不仅能够在管道(即主域)中的最高优先级域的上下文中运行,如基于内核的Xenomai线程,而且还能够在常规Linux空间(即辅助域)中运行,尽管Xenomai仍然被认为是实时的,但遭遇更高的调度延迟。在Xenomai的术语中,前者据说以主要执行模式运行,而后者则经历二级执行模式。
在这里插入图片描述
为了向在辅助域中运行的线程提供完整的实时支持,Xenomai需要实现以下功能:

  • 通用优先级机制(Common priority scheme)。在调度方面,我们需要一种方法让实时核和Linux内核共享相同的优先级方案,相对于它们共享控制权的线程集; 换句话说,Xenomai线程应该在所有现有Xenomai线程中随时正确执行其优先级,无论其当前域如何。Xenomai应用它所谓的根线程的可变优先级技术,Linux内核通过该技术自动继承由进入二级域的实时核心控制的Xenomai线程的优先级。实际上,这意味着当前在主域中运行的Xenomai线程不一定会抢占在辅助域中运行的线程,除非它们的有效优先级实际上更高。上述行为与RTAI / LXRT所发生的情况相反,例如,通过继承RTAI调度程序定义的最低优先级,迁移到Linux空间的线程实际上在同一个移动中失去了它们的实时优先级。这就是说,Xenomai不知道的常规Linux任务,只碰巧属于SCHED_FIFO类,在与来自主要Xenomai域的Xenomai线程竞争CPU时总会被抢占,尽管他们仍然会优先与Xenomai线程竞争在辅助域中运行。

  • 程序执行时间的可预测性(Predictability of program execution times)。当Xenomai线程在Linux(即辅助)域上运行时,无论是执行内核还是应用程序代码,其时序都不应受非实时Linux中断活动的干扰,一般来说,任何低优先级的异步活动都会发生在内核级别。防止后者发生大多数机会的一种简单方法是在Xenomai线程在Linux域中运行时使Linux内核免于中断,以便在此期间不会从上半部分中断处理程序触发延迟后处理时间 让Linux内核免受中断困扰的一个简单方法是在中间的Adeos域中需要时阻塞它们,这些域位于实时核心和Linux内核之间,这在Xenomai的术语中被称为中断屏蔽。每当Linux内核调度Xenomai线程时都会使用此屏蔽,并在所有其他情况下脱离。应该注意的是,屏蔽支持可以在每个线程的基础上启用/禁用,或者在Xenomai构建时在系统范围内启用/禁用; 默认情况下,它是针对Xenomai线程禁用的,而不是内置的。

  • 细粒度的Linux内核(Fine-grained Linux kernel)。为了从辅助执行模式中获得最佳效果,我们需要Linux内核展示尽可能短的不可抢占部分,以便在辅助域中运行的Xenomai线程准备就绪后尽快重新安排机会。 -跑。此外,这确保Xenomai线程可以在短时间限制的时间段内从主域迁移到辅助域,因为此操作涉及到达内核重新安排点。出于这个原因,Xenomai受益于Linux内核整体可抢占性的持续改进趋势,包括Ingo Molnar的PREEMPT_RT扩展。当然,只在主域运行的Xenomai线程不受Linux内核粒度级的影响,

  • 优先倒置管理(Priority inversion management)。实时核心和Linux内核都应该处理高优先级线程不能运行的情况,因为低优先级线程在可能无限制的时间内保存竞争资源。Xenomai提供了这种支持,但只有PREEMPT_RT变体才能为Linux内核提供支持。出于这个原因,Xenomai密切关注并为PREEMPT_RT的当前发展提供支持,尽管主线内核仍然是目前的参考系统。

由于上述要求,当加载Xenomai核心时,底层的Adeos管道包含三个阶段,所有中断都按优先级顺序流动:

  • Xenomai的主要领域,它是实时核心的所在地;
  • 中断屏蔽域;
  • Linux域名。

系统调用拦截

由于可以在Xenomai核上堆叠的实时API(即皮肤)可以将自己的一组服务导出到用户空间中的Xenomai线程,因此必须有一种方法可以正确地调度相应的系统调用常规Linux内核系统调用,适当的处理程序。为此,Xenomai拦截Xenomai线程代表任何Xenomai或Linux域发出的每个系统调用陷阱/异常。这可以通过使用适当的Adeos服务订阅事件处理程序来实现(有关更多信息,请参阅 adeos_catch_event()服务,指定
ADEOS_SYSCALL_PROLOGUE/IPIPE_EVENT_SYSCALL事件时)。Xenomai使用此功能:

  • 将应用程序的实时服务请求分派给适当的系统调用处理程序,这些处理程序由运行在实时核心上的各种API实现;
  • 通过根据需要将调用方无缝迁移到目标域,确保在适当的域(Xenomai或Linux)的控制下执行每个系统调用。例如,在将请求中继到常规Linux系统调用处理程序之前,从Xenomai域中运行的Xenomai线程发出的Linux系统调用将导致调用方自动迁移到Linux域。相反,在最终执行服务之前,调用可能阻塞的Xenomai系统调用的Xenomai线程将被移动到Xenomai域,以便调用者可以在实时核的控制下睡眠。

两者的结合使Xenomai线程特别好地集成到Linux领域。例如,Xenomai和常规Linux应用程序的通用系统调用路径使前者显示为后者的自然扩展。作为对此的一个例证,Xenomai线程都支持完整的Linux信号语义和ptracing功能,这反过来又为它们本身提供了GDB支持。

中断传播

因为它在管道中处于领先地位,所以存在于Xenomai域中的实时核心首先被通知任何感兴趣的传入中断,处理它,然后将这样的中断标记为传递到管道,最终传递到Linux内核域,如果需要的话。

当从传入中断通知时,实时核在外部中断处理程序返回后重新调度(如果中断堆积),并切换它控制的最高优先级可运行线程。

当没有实时活动待处理时,Xenomai域将CPU发送到中断屏蔽域,这反过来只要它被释放到Linux内核就会通过它,或者如果它被占用则阻塞它们。Adeos通过管道有两种基本的中断传播模式:

  • 在隐式模式下,任何传入的中断都会被Adeos自动标记为待处理的每个接收域的日志接受中断源。
  • 在显式模式下,如果中断处理程序需要,则必须“手动”将中断传播到管道下的邻居域。

此设置基于每个域,每个中断定义。Xenomai始终对其截获的所有中断使用显式模式。这意味着每个处理程序必须调用显式传播服务以将传入中断传递到管道。当Xenomai没有为给定的中断定义处理程序时,中断无条件地传播到Linux内核:当实时核不拦截这样的中断时,这使系统保持工作。

启用/禁用中断源

除了能够完全停止域以便在明确未安装之前不再有中断流过它之外,Adeos还允许在硬件级别选择性地禁用和相反地重新启用实际的中断源。

接管后,Adeos处理所有域的中断禁用请求,包括Linux内核和实时核心。这意味着在硬件PIC级别禁用中断源,并锁定从该源到管道级别的当前域的任何中断传递。相反,启用中断意味着重新激活PIC级别的中断源,并允许从此源进一步传送到当前域。因此,启用中断源的域必须与禁用它的域相同,因为此类操作依赖于域。

实际上,这意味着,成对使用时,rthal_irq_disable()和rthal_irq_enable()服务集成了构成了 Xenomai基础的实时HAL内相关Adeos的调用,必须由同一Adeos域解决。例如,如果一个实时中断处理程序用 rthal_irq_request()服务于某个中断源连在一起,禁用中断源使用rthal_irq_disable(),那么这个源将会被 Xenomai域阻塞直到同一中断源的rthal_irq_enable()被同一域调用。处理这个请求失败将会导致受影响的中断通道永久性丢失。

在域之间共享中断

一个在域间共享硬件中断时误用Adeos管线的典型例子如下:

 void realtime_eth_handler (unsigned irq, void *cookie)
 {
     /*
      * This interrupt handler has been installed using
      * rthal_irq_request(), so it will always be invoked on behalf of
      * the Xenomai (primary) domain.
      */

     rthal_irq_disable(irq);
     /* The Xenomai domain won't receive this irq anymore */

     rthal_irq_host_pend(irq);
     /* This irq has been marked as pending for Linux */
 }
 
void linux_eth_handler (int irq, void *dev_id, struct pt_regs *regs)
{
    /*
     * This interrupt handler has been installed using
     * rthal_irq_host_request(), so it will always be invoked on
     * behalf of the Linux (secondary) domain, as a shared interrupt
     * handler (Linux-wise).
     */
    rthal_irq_enable(irq);
   
    /*
     * BUG: This won't work as expected: we are only unlocking the
     * interrupt source for the Linux domain which is current here,
     * not for the Xenomai domain!
     */

在上面的这个不工作的例子中,由于Xenomai始终对所有中断使用显式传播模式进行拦截,因此下一个以太网中断将仅在Xenomai日志中标记为挂起,等待Xenomai处理程序可能将其手动传播到Linux 。但是因为Xenomai的中断仍然被锁定在管道级别(请记住,没有人实际发出rthal_irq_enable() Xenomai域的预期,但只是错误地从Linux中发出),这不会发生,因为Xenomai处理程序将不会运行直到锁被删除。因此,好吧,chickenandegg问题:我们敬酒。

幸运的是,有一种解决方案可以在需要保持中断源禁用的域之间正确共享中断,直到完成最终处理(例如,处理级别触发的中断是其中一个问题):实际上,您不需要做任何事情,因为Adeos已经屏蔽了PIC级别的任何输入中断,然后再向其提供管道。因此,您只需要根据相应的域处理程序处理中断,并确保使用最后一个中断源重新启用 rthal_irq_enable()。只要Linux内核是其中一个收件人,常规内核处理程序就会自动重新启用,所以基本上,你只需要打扰调用 rthal_irq_enable()处理程序,这些处理程序不会将传入的中断传播到Linux内核的下游。

特别是在x86架构上,由于性能原因,在Adeos收到定时器中断时没有被屏蔽。这就是说,定时器源不是你可能想要以任何方式禁用的定时器源,所以这是一个非问题。

中断共享和延迟

但是,在通过整个流水线进行传播时保持屏蔽中断源可能会增加延迟。

由于Adeos保证不会因为在任何给定域上堆积的中断而发生堆栈溢出,并且因为它在触发中断处理程序之前也会使当前阶段停止,所以不需要在Xenomai处理程序中禁用中断源。相反,您甚至可能希望重新启用它,以便可以立即记录更多事件,并在当前处理程序调用返回后立即播放。

因此,解决方案是以这种方式重写前一个示例,这次正确:

void realtime_eth_handler(unsigned irq,void * cookie)
{
    rthal_irq_enable(IRQ);
    rthal_irq_host_pend(IRQ);
    / *此irq已被标记为待处理Linux * /
}

void linux_eth_handler(int irq,void * dev_id,struct pt_regs * regs)
{
    / *正常处理IRQ。* /
}

结论

Adeos是一段相当简单的代码,在正确使用时具有非常有趣的属性。Adeos方案的主干是事件管道,因此,它带来了我们在Xenomai中需要的所有关键功能:

  • 可预测的中断延迟;
  • 精确的中断虚拟化控制(每域和每中断处理程序注册,每域和每个CPU中断屏蔽);
  • 事件的统一,优先和面向域的传播方案;
  • 一种通用且简单的API,可简化客户端代码的可移植性。

Xenomai使用这些功能来寻求最佳的Linux内核实时服务集成。Xenomai的主要模式可在最短的微秒延迟范围内提供真正的实时性能。此外,Xenomai对未来的Linux演进进行了投注,如PREEMPT_RT,以提高内核的整体粒度,以便辅助模式在确定性意义上仍然是实时的,尽管有更高的最坏情况延迟。这就是为什么Xenomai从第一天起就努力工作以达到与Linux内核紧密集成的水平。共生思想,寻求共生。

Xenomai用户层实时的实现(TSC)

Xenomai除了在内核层利用Adeos实现了硬实时外,它在用户空间也有很好的实时性。在S3C2410平台上,为了实现用户层的实 时,Xenomai实现了一个硬件计数器——Decrementer。这个硬件计数器可以在用户空问里很好地模拟TSC(Time Stamp Counter,时间戳计数器)。

同时,Xenomai在Linux内核中加入了一个全新的数据结构__ipipe_tscinfo,可以通过此数据结构变量存放用户层需要的数据。该数据结构组成如下:

  • 在用户层,应用程序通过系统调用可以迅速得到struct_ipipe_tscinfo结构体中的数据。而且为了避免受到缓存的影响,Xenomai将此结构体变量存放在Linux的向量页中。
  • 内核通过函数_ipipe_mach_get_tscinfo来填充struct_ipipe_tscinfo结构体变量中的各项内容:
    info一>typte说明在S3C2410平台上TSC是基于Decrementer硬件计数方式的;
    info 一>u.dec.counter用来将Decrementer计数器的物理地址设定为0x51000038;
    info 一>u.dec.mask掩码用来注明使用Dec—rementet。计数器中的特定位;
    info一>u.dec.tsc指向存放64位 TSC值的区域。

在Xenomai用户层的实时程序运行时,程序都会通过系统调用得到内核填充好的struct_ipipe_tscinfo结构体变量。具体实现可参考编 译用户层实时程序时用到的,由Xenomai所提的头文件/usr/xenomai/include/asm/syscall.h。

Xenomai多API构架(skin)

除了提供Linux硬实时,Xenomai的另一个目的是使基于Linux的实时操作系统能提供与传统的工业级实时操作系统(包括VxWorks、 pSOS+、VRTX或者uITRON)功能相同的API。这样,可以让这些操作系统下的应用程序能够很容易地移植到GNU/Linux环境中,同时保持 很好的实时性。
Xenomai的核心技术表现为使用一个实时微内核(real—time nucleus)来构建这些实时API,也称作"skin"。在实时核复用的基础上,一个skin可以很好地模拟一种实时操作系统的API。它的结构图可以参考图2。
在这里插入图片描述在这里插入图片描述

图2中,Native是Xenomai自带的API,各类API都有着同等的地位,都独立地基于同一个实时微内核。这样做可以让内核的优点被外层所有的 API很好地继承下来。更重要的是,实时微内核提供的服务被外层各种API以不同的方式表现出来,由此可以增强整个系统的强壮性。

  • 编制实时程序时,在很多实时操作系统上只能在内核层实现;
  • 而编制实时内核模块时,会受到内核的限制,比如有些实时内核不支持浮点运算,模块出错时容易使整 个系统挂起,而且内核模块的调试比较困难。
  • Xenomai能够支持较好的用户层实时,这为编制实时性要求不是非常高的实时程序提供了一个有效途径。

下面这 个用户层实时例程使用的是Xenomai提供的Native API:

从程序中可以看出,Xenomai的用户层实时程序的周期可以轻易地设定到μs级,所以它完全可以适用于一般实时性要求的工程应用。

Xenomai的应用接口

Dispatching problem

Cobalt和传统的libc都实现了POSIX接口,RT实时应用怎么来选择呢?

• Both Cobalt and libc provide POSIX implementations
• How do RT application pick the right one?

Solution: symbol wrapping

使用符号包装的方式来解决,给符号首先加上"_wrap"开头给libcobalt解析,对于不能解析的符号再传递给libc解析.

• Example pthread_mutex_lock → __wrap_pthread_mutex_lock
• libcobalt provides __wrap_*, forwards unhandled invocations to libc
• No source code changes to POSIX applications required
• Some additional services available (*_np)

Supported architectures

• ARM (32 bit, 64 bit upcoming)
• Blackfin
• PowerPC (32 bit, 64 bit)
• x86 (32 bit, 64 bit, 32-on-64 bit, x32)

在Cobalt和LInux之间迁移线程

Preserve Linux service for Cobalt threads

怎么样为Cobalt线程保留Linux服务?

• Linux syscalls
• Fault and trap handling
• Handling of asynchronous signals

Solution: every cobalt thread is also a Linux task

每个colbalt线程同时也是一个Linux进程,共享线程状态,但是同一时刻只能是cobalt/linux其中的一种。

• Share thread states
• Only one can run at a time
• Migration to RT: suspend Linux task, resume Cobalt thread
• Migration to Linux (on syscall, fault/trap, signal): suspend Cobalt thread, resume Linux task

Real-Time Driver Model (RTDM)

Goals and principles

• Provide environment for co-kernel real-time drivers
• Service interface towards applications and other drivers
• Low-level primitives from implementing drivers
• Reuse Linux for non-RT purposes
(setup / shutdown, resource discovery and claiming, etc.)

Two types of RTDM devices

• Character device (open/close, read, write, ioctl)
• Protocol device (socket, bind, send, recv, etc.)

Device profiles

• Character: UART, UDD (analogous to UIO), Memory, ...
• Protocol: UDP/TCP (RTnet), CAN, IPC, ...

Tooling with Cobalt

Debugging

• gdb works
• Improvements on synchronous stop/resume are work in progress

Tracing

• ftrace (tracecmd & Co.)
• I-pipe latency tracer (low-level latency hunts)

Valgrind/Helgrind

• No support because of unknown syscalls
• Alternative: Mercury (native support)
• Limited suitability for RT applications in general

Xenomai 和 Linux 的关系

xenomai的核心思想是实时性:

中断处理:ipipe。
调度:Aedos域的调度使用cobalt/nuclear,linux域的调度使用linux自己的scheduler。
系统调用:
应用接口:skins。提供兼容传统RTOS的API支持,如RTAI、POSIX、VxWorks、uITRON、 pSOS+等。
驱动模型:RTDM。
定时器:

除此以外,要保持扩展性和兼容性:

启动:使用linux的启动。
进程:进程创建和进程空间管理,使用linux现成机制。实时进程可以切换到linux普通进程。
内核API:实时进程可以调用linux内核的普通API。调用时会发生模式切换。
内存分配:使用Linux的内存分配机制。

1.4 实时linux基础

  • 实时Linux如何工作?

实时(RT)Linux的总体思想是,小型实时内核在Linux下运行,这意味着实时内核比Linux内核具有更高的优先级。 实时任务由实时内核执行,并且在不需要执行实时任务的情况下允许运行普通的Linux程序。 Linux可被视为实时调度程序的空闲任务。 当此空闲任务运行时,它将执行自己的调度程序并调度正常的Linux进程。 由于实时内核具有更高的优先级,因此当实时任务准备好运行并立即执行实时任务时,通常的Linux进程将被抢占。
在这里插入图片描述在这里插入图片描述

  • 实时内核如何比Linux内核具有更高的优先级?

基本上,操作系统是由中断驱动的,可以将其视为计算机的心跳:

•OS中运行的所有程序均由调度程序调度,该调度程序由时钟的计时器中断驱动,以在特定时间重新调度。
•执行程序可以阻止或自愿放弃CPU,在这种情况下,通过软件中断(系统调用)通知调度程序。
•硬件可以生成中断来中断OS的正常计划工作,以快速处理硬件。

RT Linux使用中断流为实时内核提供比Linux内核更高的优先级:

•当中断到达时,首先将其分配给实时内核,而不是Linux内核。但是存储了中断,以便在实时内核完成后将其提供给Linux。
•作为第一排,实时内核可以运行由这些中断驱动的实时任务。
•仅当实时内核什么也不运行时,存储的中断才传递给Linux内核。
•作为第二排,Linux可以调度由这些中断驱动的自己的进程。

因此,当正常的Linux程序运行并且有新的中断到达时:

•它首先由实时内核设置的中断处理程序处理;
•中断处理程序中的代码唤醒实时任务;
•在中断处理程序之后,立即调用实时调度程序;
•实时调度程序观察到另一个实时任务已准备就绪,可以运行,因此它将Linux内核置于睡眠状态并唤醒该实时任务。

因此,需要一种在实时内核和Linux内核之间传递中断的特殊方式。每种RT Linux都以其自己的方式进行此操作。 Xenomai使用Adeos项目中的中断管道。有关更多信息,另请参阅《 Adeos的生活》。

  • Linux内核系统调用与实时内核系统调用

如果程序希望完成某件事,但没有正确的特权操作模式,则通常的方法是生成特殊的软件中断(称为系统调用)。
程序被中断,并且中断被提供给内核,内核将根据程序的行为进行操作。中断中给出的消息确定必须执行哪个操作或哪个系统调用。系统调用的集合可以看作是内核的用户程序接口。
对于实时Linux,我们有两个运行的内核,即普通Linux内核和实时内核。对于每个特定内核,分配一个唯一的软件中断。因此,根据唯一的中断号,我们知道它是Linux内核的系统调用还是实时内核的系统调用。因此,我们有:

•一组Linux系统调用,使您可以调用内核API的一部分
•一组实时系统调用,使您可以调用实时API的一部分

注意:因为实时内核首先获取中断,所以实时系统调用将始终首先运行。

  • RT Linux任务不是Linux程序

观察实时任务和标准Linux程序之间的区别:

•在实时Linux中,我们可以通过对实时线程进行编程来制作实时程序。实时Linux中的非实时程序仅使用常规Linux线程。
•实时任务可以使用内核模块在内核空间中执行,但也可以使用普通的C程序在用户空间中执行。
•在用户空间而不是内核空间中运行会产生一些额外的开销,但是具有以下优点:
    o内核模块中的错误可能会使整个系统崩溃,但是用户空间程序中的错误只会使程序崩溃。
    o在内核模型中,只能使用实时API和有限的内核API,但是在实时用户空间程序中,可以使用实时API和整个Linux API。但是,在用户空间中使用Linux API时,该程序不能由实时调度程序(HARD实时)调度,而必须由Linux调度程序(SOFT实时)调度。因此,从实时用户空间任务调用Linux API调用会将其性能从HARD实时降低为SOFT实时。调用之后,任务将返回到实时调度程序。
    o在用户空间中,您可以使用调试器调试实时程序。 (例如,针对C程序员的gdb)。
  • 用户空间Xenomai开销与内核空间相比

将用户空间中的Xenomai任务与内核空间中的Xenomai任务进行比较,观察到通过交换虚拟内存没有开销:

•内核空间和用户空间都使用虚拟内存,因此两者都必须在页表中查找实际内存
•Xenomai内核空间任务和Xenomai用户空间任务均被内存锁定:
    1.内存的内核部分是锁定内存,这意味着它永远无法交换到磁盘。 因此,内核模块自动使用的内核内存永远不会交换到磁盘。
    2.内核空间中Xenomai任务的内存也无法交换到磁盘。 用户空间中的Xenomai任务是程序员在启动时锁定的内存。

Xenomai API函数有一些开销:

•用户空间Xenomai任务需要使用Xenomai系统调用来执行位于内核空间中的Xenomai API函数。
•内核空间的Xenomai任务只能对Xenomai API函数进行函数调用。

在这里插入图片描述在这里插入图片描述

1.5 RTAI vs Xenomai

在这里插入图片描述

图1所示为RTAI和Xenomai两个实时内核分别与标准Linux内核组成双内核系统是的分层结构。可以看到两者有稍微不同的组织形式,与 Xenomai让ADEOS掌控所有的中断源不同的是,RTAI拦截它们,使用ADEOS将那些RTAI不感兴趣的中断通知送给Linux(也就是,中断 不影响实时时序)。这样混合过程的目的是提高性能,因为在这种情况下,如果中断是要唤醒一个实时任务,就避免了由ADEOS管理中断的开销。从这里可以看 出,RTAI的实时性能应该是比Xenomai要好的。

RTAI(Real-Time Linux Application interface)虽然实时性能较好,但对ARM支持不够,更新速度极慢,造成项目开发周期长,研发成本高。

与RTAI相比,Xenomai更加专注于用户态下的实时性、提供多套与主流商业RTOS兼容的API以及对硬件的广泛支持,在其之上构建的应用系统能 保持较高实时性,而且稳定性和兼容性更好;此外,Xenomai社区活跃,紧跟主流内核更新,支持多种架构,对ARM的支持很好。

**Xenomai**是Linux内核的一个实时开发框架。它希望无缝地集成到Linux环境中来给用户空间应用程序提供全面的、与接口无关的硬实时性Xenomai是基于一个抽象实时操作系统核心的,可以被用来在一个通用实时操作系统调用的核心上,构建任意多个不同的实时接口。Xenomai项目始于 2001年8月。2003年它和RTAI项目合并推出了RTAI/fusion。2005年,因为开发理念不同,RTAI/fusion项目又从RTAI 中独立出来作为Xenomai项目。相比之下:

  • RTAI项目致力于技术上可行的最低延迟;
  • Xenomai除此之外还很着重扩展性、可移植性以及可维护性。 Xenomai项目将对Ingo Molnar的PREEMPT_PT实时抢占补丁提供支持,这又是与RTAI项目的一个显著的不同。RTAI和Xenomai都有开发者社区支持,都可以作为一个VxWorks的开源替代。

Xenomai是基于Adeos(Adaptive Domain Environment for Operating System)实现的,Adeos的目标是为操作系统提供了一个灵活的、可扩展的自适应环境;在这个环境下,多个相同或不同的操作系统可以共存,共享硬件 资源。基于Adeos的系统中,每个操作系统都在独立的域内运行,每个域可以有独立的地址空间和类似于进程、虚拟内存等的软件抽象层,而且这些资源也可以 由不同的域共享。与以往传统的操作系统共存方法不同,Adeos是在已有的操作系统下插入一个软件层,通过向上层多个操作系统提供某些原语和机制实现硬件 共享。应用上主要是提供了一个用于“硬件-内核”接口的纳内核(超微内核),使基于Linux环境的系统能满足硬实时的要求。

Xenomai正是充分利用了Adeos技术,它的首要目标是帮助人们尽量平缓地移植那些依赖传统RTOS的应用程序到GNU/Linux环境,避免全部 重写应用程序。它提供一个模拟器模拟传统实时操作系统的API,这样就很容易移植应用程序到GNU/Linux环境中,同时又能保持很好的实时性。 Xenomai的核心技术就是使用一个实时微内核来构建这些实时API,也称作“Skin”。Xenomai通过这种接口变种技术实现了针对多种传统 RTOS的应用编程接口,方便传统RTOS应用程序向GNU/Linux的移植。图2描述了Xenomai的这种带Skin的分层架构。
在这里插入图片描述
从图2可以看出,Xenomai系统包含多个抽象层:Adeos纳内核直接工作在硬件之上;位于Adeos之上的是与处理器体系结构相关的硬件 抽象层(Hardware Abstraction Layer, HAL);系统的中心部分是运行在硬件抽象层之上的抽象的实时内核,实时内核实现了一系列通用RTOS的基本服务。这些基本服务可以由Xenomai的本 地API(Native)或由建立在实时内核上的针对其他传统RTOS的客户API提供,如RTAI、POSIX、VxWorks、uITRON、 pSOS+等。客户API旨在兼容其所支持的传统RTOS的应用程序在Xenomai上的移植,使应用程序在向Xenomai/Linux体系移植的过程 中不需要完全重新改写,此特性保证了Xenomai系统的稳健性。Xenomai/Linux系统为用户程序提供了用户空间和内核空间两种模式,前者通过 系统调用接口实现,后者通过实时内核实现。用户空间的执行模式保证了系统的可靠性和良好的软实时性,内核空间程序则能提供优秀的硬实时性。

**skin**提供了兼容传统RTOS的API封装,如RTAI、POSIX、VxWorks、uITRON、 pSOS+等。

1.6 Xenomai

双核(如RTLinux,RTAI,Xenomai)实时方案。运作一个real-time核心,然后将修改过的GNU / Linux核心程序码视为该实时核心的空闲任务。

在xenomai中,双内核就是Xenomai的Nucleus/Cobalt CoreLinux内核
Xenomai改变整个系统架构,让ipipe - > xenomai scheduler来预先处理实时任务,而Linux则拉到上层成为一个任务。这样可以避免Linux因为庞大的架构而影响处理实时的时间。

Xenomai系统架构

xenomai的软件版本已经由2.xx版本过渡到3.xx,架构发生了较大变化,主要在实时内核不再是原来的Adeos,改由现在的cobalt。

以下是Xenomai 2.6.4 架构:
在这里插入图片描述
Xenomai是一个linux内核的补丁藉由在底层增加一个架构负责硬体与接收中断并将中断传给上层的OS(这边称为域)。Xenomai的两个域:

  • 域1:Xenomai。调度核心(scheduler)为Nucleus/Cobalt,应用接口为Skin。xenomai2的调度器为Nucleus,xenomai3的调度器为Cobalt。
  • 域2:Linux。调度核心(scheduler)为Linux原生,应用接口为Linux原生。

这个底层的架构Adeos是另一个开源的项目。

在API呼叫上可以看到不同层级的抽象化:

ipipe_XXX -> rthal_XXX -> xnXXX

负责传送中断的程式称为ipipe示意图:
在这里插入图片描述
可以找到ipipe_raise_irq()将中断推到管道。在ipipe上每个域都有自己的优先度,高优先度的域会先接收到中断,高优先度的域的线程可以preempt低优先度域的线程。

interrupt shield是中断域盾的意思。

xenomai3的两种配置

  • Cobalt:采用双核架构,是xenomai 2的延伸。
  • Mercury:使用单内核形式,在linux内核上提供xenomai api,由于本身依赖linux,一般来说会会以及PREEMPT_RT提供实时服务。

Xenomai 3双核配置:Colbat(钴)
在这里插入图片描述
多一个优先比linux还高叫钴的核心去处理实时的事情,提供不同的实时API给不同的应用程序使用。并且利用乐观中断保护机制减少改变中断掩码,一般的机制在每次进入关键部分时都要中断屏蔽,而乐观中断保护可以不用。
而实时在意的“截止日期”,实际上就是探讨延迟(延迟越大,系统越难在时内内完成完成高优先于任务,自然即时能力就越差),而延迟很大的来源则是中断处理。

通过使用与它并行运行的实时协同内核来补充Linux。这个名为Cobalt的小扩展内置于Linux内核中,处理所有时间关键活动,例如处理中断和调度实时线程。Cobalt核心优先于本机内核活动。

在这种双内核配置中,所有RTOS API Xenomai都提供与Cobalt核心的接口,并且只有那些API被认为是实时的,包括由Xenomai(aka libcobalt)实现的POSIX 1003.1c服务子集。

Xenomai 3单核配置:Mercury(汞)
在这里插入图片描述

运用本机的linux core在PREEMPT_RT之上达到real-time的事情,这里不是强制的,看应用程序对反应时间和最大抖动的要求,有些甚至会作到某种程度截止日期的忽略。

依靠本机Linux内核的实时功能,形成Mercury核心。通常,应用程序需要在目标内核中启用PREEMPT-RT扩展,以提供实时服务。

但是,这不是强制性的,它取决于响应性和最大抖动的应用要求; 有些人甚至可以容忍一定比例的截止日期未命中。

在这种单内核配置中,Xenomai提供的所有非POSIX RTOS API都可以通过本机线程库(最好是NPTL,但也支持传统设置的linuxthreads)进行精确模拟。

Adeos/iPipe

主要负责处理irq与高分辨率计时器,ipipe的工作很简单就是设定计时器并将中断往上丢

  • Adeos功能

  • 事件管道:

利用管道的方式将不同域的中断或系统调用往上丢

  • 乐观的中断保护:

当同一个域在处理中断时,有跟她相同域的中断要进来时,会将它进入待定状态,等到所有待中断完成时,才会处理下个域的中断。但更高优先权域的中断会抢占较低优先权域的中断。

  • 乐观与原生中断:

原生:
在这里插入图片描述
乐观:
在这里插入图片描述
从前两张图可看出一般的机制在每次进入临界区时都要中断掩码,而乐观中断保护可以不用,所以在中断管理的时间差很多。而实时在意的“deadline”,实际上就是是探讨延迟(延迟越大,系统越难在时内内完成完成高优先任任务,自然即时能力就越差),而延迟很大的来源则是中断处理。

  • 系统事件传播:

系统事件(例如:页面错误句柄)传递方式不同于interrupt,基本上是不可被stall的。

  • 实时支持在辅助域中运行的线程

  • 共同优先方案:

当xenomai任务迁移到linux域时,linux域会继承xenomai任务的优先级。
在这里插入图片描述

  • 程序执行时间的可预测性:

当xenomai线程进入linux(二级)域时,不可被linux域中断抢占掉,也不能被其他低优先级活动在linux内核抢占掉。通常最简单实作方式就是加一个中断盾域。
在这里插入图片描述

  • 细粒度的Linux内核:

为了从二级域中获得最佳效果,我们需要Linux内核展示尽可能短的不可抢占部分,以便在二级域中运行的Xenomai线程准备好运行后尽快重新安排机会。

  • 优先倒置管理:

实时核心和Linux内核都应该处理高优先级线程不能运行的情况,因为低优先级线程在可能无限制的时间内保持满足的资源。

  • 相关文件︰
gic.c(旧版) - > irq-gic.c(新版):
通用中断控制器(GIC)为ARM架构中负责分配interrupt至cpu的装置。此档案实作gic功能的界面,包含init,mask,产生软件中断,中断结束,取得资讯等。内容除了gic register操作外,也包含了旋转锁。

it8152.c:提供ITE8152(PCI Bridge)的支援。目前该硬体已经停止生产。

timer-sp.c:ARM双定时器模块(SP804)的界面.SP804提供两个32 / 64bit倒计数器,并提供定时器中断。

vic.c - > irq-vic.c:
提供向量中断控制器(VIC)的界面.VIC主要存在于armv6或以前的架构中,提供优先级,IRQ向量等功能,但并不支持SMP。在armv7之后的架构中,其渐渐被NVIC(Cortex-M) )与GIC(皮质-R / A)取代。

ipipe-tsc.c - > ipipe_tsc.c:Time Stamp Counter的界面,提供自复制起循循数的计算。

ipipe / compat.c:与I-pipe遗留接口相关。

sched / clock.c:取得cpu_clock解析度为nanosecond,开机后从0开始上数。在新版(3.18)ipipe中,此档案并无修改。

在这里插入图片描述

以上是一个GIC的示意图。

HAL

档案位置在:xenomai-head / ksrc / arch / arm / hal.c(xenomai 2.6)

硬件抽象层:进程透过HAL呼叫ipipe的服务。这一层主要是包装ipipe与底层资讯让nucleus可以不用看到硬体资讯。

核/钴(Cobalt)

档案位置在:xenomai-head / ksrc / nucleus(xenomai 2.6); xenomai-head / kernel / cobalt(xenomai 3)

Xenomai的内核,包含调度,定时器,同步,线程,锁等等一般该有的RTOS功能,负责实时任务的执行。

调度(scheduler)

优先处理实时任务,linux也被视为其中一个线程,本身也有调度器,但须等到没有实时任务时(空闲状态),才会执行linux thread。
在这里插入图片描述
就文件有五个关于sched.c中应该有四种不同的调度方式:

sched-idle.c:是专门处理idle state态给linux schedule使用
sched-rt.c:给实时调度程序使用(FIFO + RR)
sched-sporadic.c:POSIX SCHED_SPORADIC调度类。
sched-tp.c:时间分区(典型的IMA系统)
sched.c:应该是负责四个schedule方式的档案

用户接口(skins)

档案位置在xenomai-head / ksrc / skins(xenomai 2.6)

呼叫xenomai的接口,有本地rtdm posix psos + uitron vrtx vxworks等。

Xenomai与PREEMPT_RT的差异

Linux内核抢占模型组态(实时程度↑=>延迟↓但吞吐量↓):

- PREEMPT_NONE

着重公平和吞吐量,过程在执行系统呼叫时无法被抢占。

- PREEMPT_VOLUNTARY(桌面)

允许一个低优先权的进程把自己preempt掉(即便该进程正在内核模式执行着一个系统呼叫)
PREEMPT(低延迟桌面)

- PREEMPT_RT

着重决定论,对即时系统而言,作业系统的“可决定性”比吞吐量更为重要,使用固定优先权的先发制人排程策略(POSIX SCHED_FIFO与SCHED_RR)。

PREEMPT_RT机制:

可抢占的关键部分
可抢占的中断处理程序
可抢占的“中断禁用”代码序列
内核中的自旋锁和信号量的优先级继承
延期经营
延迟减少措施

在“可调度/线程”上下文中执行所有活动(包括IRQ),IRQ处理程序移到线程中执行。

优先级继承:是让握有自旋锁或信号量的进程可以暂时的提高优先权让他可以尽快做完关键部分释放自旋锁或信号量,高优先的流程才有办法继续执行。

PREEMPT_RT与xenomai的差异:

RT_PREEMPT是基于linux架构去改进让更多地方能preempt达到real-time的能力

Xenomai则是改变整个系统架构新增一个调度员与IRQ管理的机制,让处理实时任务流程简化到只剩ipipe-> scheduler就能执行,不会因linux庞大的架构影响到实时任务的处理时间。

RTDM

The Real-Time Driver Model (RTDM) provides a unified interface to both users and developers of real-time device drivers. Specifically, it addresses the constraints of mixed RT/non-RT systems like Xenomai. RTDM conforms to POSIX semantics (IEEE Std 1003.1) where available and applicable.
实时驱动程序模型(RTDM)为实时设备驱动程序的用户和开发人员提供了统一的接口。 具体来说,它解决了混合实时/非实时系统(如Xenomai)的局限性。 RTDM在可用且适用的情况下符合POSIX语义(IEEE Std 1003.1)。

1.7 Xenomai资源

task latency的产生原理

在这里插入图片描述

GIC原理

在这里插入图片描述

GIC:通用中断控制器(GIC)是arm用来集中分配interrupt至cpu的装置。它主要分配给分配器和cpu接口。

  • distributor:负责分配中断,纪录执行状态,并提供寄存器以决定每个中断的启用,优先级,目标处理器。每个中断会有固定的中断ID,以供接收的cpu辨认。
  • cpu interface:向cpu传送中断请求,并提供分配器接收(确认)中断,完成中断等信息;它也提供决定优先级掩码,抢占策略的寄存器。

当启动时,cpu接口会收到优先最高的待处理中断,并决定它是否有足够的优先级此cpu执行(参考掩码,运行中断,抢占),若是则signal cpu.cpu读取接口的寄存器(GICC_HPPIR) )以接收中断,此接取会得到中断ID,当接收后分配会改变状态(由pending-> active(和pending));完成之后,cpu写入寄存器以示意中断已经完成。
中断类型:

  • 专用外设中断(PPI)ID:16-31。每个CPU各自独立的硬体中断。
  • 共享外设中断(SPI)ID:32~1019。外部硬体中断。
  • 软件生成的中断(SGI)ID:0~15。软体中断,由一个cpu发出,可指定至一个或多个cpu,cpu以写入GICD_SGIR的方式产生SGI,其中PPI与SGI是NN模型,每个cpu的中断状态各自独立; SPI是1-N模型,一旦其中一个目标CPU接受,中断即视为已处理。

如何选择xenomai特性

  • 对于严格的实时要求,您应该考虑为目标SoC架构和Linux内核版本提供硬实时支持。
可在此处查看双内核配置中Xenomai的硬件支持。该技术基于内核补丁,该补丁引入了一种机制,用于将所有关键事件转移到与内核Linux内核耦合的双内核扩展。这个(过时的)文档详细描述了这种称为中断管道(又称I-Pipe)的机制 。可以在此站点获取可用于各种内核版本的所有I-pipe修补程序 。

对于单个内核配置,此处提供了支持完整抢占功能(也称为PREEMPT-RT) 的目标体系结构列表 。
  • 实时操作涉及多少CPU核心?
在双内核配置中,Xenomai协同内核在不超过四个CPU内核的同时处理实时活动时,通常会受益于其更简单的锁定方案。
除了运行实时活动的四个CPU核心外,单个内核配置的SMP可扩展性将更好。

关键问题在于实际运行实时线程的CPU内核数量以及从实时源接收中断,而不是目标硬件上在线核心的总数。由于Cobalt协同内核在处理实时活动时不会与常规Linux内核共享任何锁定,因此在不超过四个可用内核上固定此类活动的16路服务器仍然可以在Cobalt中提供良好的性能。基于双内核配置。
  • Xenomai双核方案(Cobalt)的考虑因素:
将Cobalt协同内核移植到新架构非常简单。
它与主线Linux内核的开发周期分离,从而允许更自由地选择所需(或所需)的内核版本。
它可以简化最坏情况分析,并使结果在托管它的主线Linux内核的更新周期内保持有效。
它允许微调非实时部分的吞吐量,而不会对实时工作产生负面影响。
它不需要对常规Linux系统进行任何调整,以保证实时作业的短暂和有限延迟。
  • Xenomai单核方案(Mercury)的考虑因素:
库存内核驱动程序可由实时应用程序重用,无需更改(前提是它们不会因不幸的实现而产生延迟问题)。另一方面,驱动程序需要专门用于在双内核配置上运行,即通过内核RTDM接口。
编程模型可能比双内核配置更简单,因为内核被认为是全局实施实时行为。这就是说,谨慎的应用程序设计和实现必须是一个通用规则,而不管底层的实时技术如何,因为并非所有可用的服务都适合实时使用。
所有用于监控系统操作的标准实用程序都可以报告开箱即用的实时活动。另一方面,双核系统必须专门为此目的调整这些工具。
  • 选择Linux内核版本:
一个移植到目标SoC或平台的Linux内核。如果可能的话,更喜欢主线内核版本而非源于供应商的版本。
在其他问题中,通过单一(PREEMPT_RT)或双内核(I-pipe)配置运行硬实时应用程序所需的内核补丁通常基于主线内核。

对于双内核配置,适合目标内核的 I-pipe补丁。
如果你没有找到任何完全匹配但又感觉幸运的补丁,你可以尝试应用针对内核发布的补丁,只是因为它的次级版本号不同,例如从3.10.22到3.10.20。但是,即使它干净利落,也需要进行一些额外的测试,从电子邮件讨论列表中获取更多信息 可能会有所帮助。

对于具有硬实时功能的单内核配置,匹配目标内核的PREEMPT-RT补丁。
  • xenomai资源:

Xenomai将使各种实时操作系统API可供基于Linux的平台使用。 当目标Linux内核不能满足响应时间限制的要求时,Xenomai还可以对其进行补充,以基于原始的双内核方法提供严格的实时保证。

Git hosting: https://xenomai.org/gitlab/xenomai
Clone: git://git.xenomai.org/xenomai.git
Maintenance branch: stable/v3.0.x
Development branch: master
  • I-pipe资源:

Cobalt实时内核依赖于主线Linux内核的补丁,该内核引入了一个单独的高优先级执行阶段,可在IRQ收到后立即运行带外中断处理程序,该处理程序不能被正常的内核工作延迟。

ARM:  https://xenomai.org/gitlab/ipipe-arm
ARM64: https://xenomai.org/gitlab/ipipe-arm64
PPC32: https://xenomai.org/gitlab/ipipe-ppc32
x86: https://xenomai.org/gitlab/ipipe-x86

2. 安装

2.1 在覆盆子(pi)的Xenomai 3

  • 取得xenomai 3.0-rc,rpi-linux 3.18.y,ipipe 3.18.12 arm patch,toolchain
git clone -b rpi-3.18.y git://github.com/raspberrypi/linux.git rpi-linux
git clone -b v3.0-rc4 git://git.xenomai.org/xenomai-3.git xenomai-3
wget http://download.gna.org/adeos/patches/v3.x/arm/ipipe-core-3.18.12-arm-1.patch
git clone https://github.com/raspberrypi/tools.git --depth=1
  • checkout rpi-linux至3.18.12
git checkout c963de6d8caec6278c0dde76831f9fdab5bace52
git checkout -b 3.18.12

由此处取得rpi post patch补丁。

  • 上ipipe补丁
cd rpi-linux
../xenomai-3/scripts/prepare-kernel.sh --arch=arm --ipipe=<你的patch位置>/ipipe-core-3.18.12-arm-1.patch   --linux=.
  • ipipe post patch(注:pre patch为解决ipipe patch冲突之用,此处无冲突故不需要)
patch -Np1 < <你的patch位置>/3.18.12-xenomai3-temp.patch
  • 配置内核(使用rpi提供的默认值)
export CCPI=($working_dir)/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-
mkdir build
make mrproper
make ARCH=arm O=build CROSS_COMPILE=$CCPI bcmrpi_defconfig
make ARCH=arm O=build CROSS_COMPILE=$CCPI menuconfig

若补丁正确,会在设定表里看到xenomai相关选项。

使用manuconfig或编辑build / .config,找到CONFIG_CPU_FREQ,CONFIG_CPU_IDLE,CONFIG_KGDB,ONFIG_CONTEXT_TRACKING_FORCE(若有的话)设为n。

  • 编译内核
make -j 5 ARCH=arm O=build CROSS_COMPILE=$CCPI
  • 安装内核模块
make ARCH=arm O=build INSTALL_MOD_PATH=dist modules_install
  • 安装头
make ARCH=arm O=build INSTALL_HDR_PATH=dist headers_install
  • xenomai
cd ../xenomai-3
mkdir dist
export PATH=/home/erickitten/workspace/xenomai/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/:$PATH
  • 设定引导
./scripts/bootstrap --with-core=cobalt –enable-debug=partial
  • 配置
./configure CFLAGS="-mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard" LDFLAGS="-mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard" --host=arm-linux-gnueabihf --with-core=cobalt
  • 安装
make DESTDIR=`pwd`/dist install
  • 复制内核
将linux-rpi / build / arch / arm / boot / Image复制到SD卡的/ boot(分区),并改名成kernel.img。或使用config.txt,以kernel =指定名称。
  • 移动模块/补丁(权限问题,需使用sudo)
cd ..
sudo cp -r rpi-linux/build/dist/lib/modules $(sdcard)/lib
sudo cp -r xenomai-3/dist/usr/xenomai $(sdcard)/usr

2.2 观察与分析

 pi@raspberrypi:~$ cat /proc/xenomai/stat
 CPU    PID        MSW                  CSW           PF         STAT            %CPU    NAME
 0      0            0                  206           0          00500080        100.0   ROOT
 0      0            0                  2688553       0          00000000        0.0     IRQ3: [timer]
  • CPU:目前这个踏是使用哪个CPU在运行,而rpi是单核心CPU,故显示皆为0
  • MSW:模式切换,此值应仅随预期与Linux服务交互的线程而增加。当进程从主模式转成二级模式或是二级模式转成主模式时,将会纪录一次的转换。cyclictest的RT任务因为会执行到memset,所以会从xenomai schedule跳到linux schedule,MSW + 1,而执行完memset后将在跳回xenomai schedule,故再+1
  • CSW:上下文切换次数(或特定CPU的IRQ命中)
  • PF:页面错误的数量(一旦mlockall生效,应该停止增加)
  • STAT:描述线程内部状态的位域。位值在include/nucleus/thread.h中定义(参见状态和模式位)。来自/proc/xenomai/sched的STAT字段给出了这些位的最重要子集的每字母1位字符符号转换。
  • %CPU:自上次检索统计信息以来线程(或IRQ处理程序)的CPU份额。
  • NAME:线程的名称(或IRQ号和注册的驱动程序)。例如,可以使用(非可移植)POSIX-API函数pthread_set_name_np进行设置。请参阅相关RTOS皮肤的API文档。
 pi@raspberrypi:~$ sudo /usr/xenomai/bin/cyclictest >/dev/null 2>/dev/null &
 [1] 2253

 pi@raspberrypi:~$ ps aux | grep -i "cy"
 root      2253  0.5  0.3   4580   1464  ?        S    03:34   0:00   sudo /usr/xenomai/bin/cyclictest
 root      2254  2.7  0.4   2340   2132  ?        SLl  03:34   0:00   /usr/xenomai/bin/cyclictest
 pi        2259  0.0  0.1   3540   820   ttyAMA0  S+   03:34   0:00   grep --color=auto -i cy

 pi@raspberrypi:~$ cat /proc/xenomai/stat
 CPU    PID        MSW                CSW              PF        STAT        %CPU    NAME
 0      0          0                  255              0         00500080    100.0   ROOT
 0      2254       1                  1                0         00b00380    0.0     cyclictest
 0      2256       2                  48               0         00300184    0.0     cyclictest
 0      0          0                  2913946          0         00000000    0.0     IRQ3: [timer]

 pi@raspberrypi:~$ watch -n 1 cat /proc/xenomai/stat
 Every 1.0s: cat /proc/xenomai/stat                      Wed Jan  8 03:38:43 2014

 CPU    PID        MSW                CSW           PF        STAT         %CPU     NAME
 0      0          0                  442           0         00500080     99.9     ROOT
 0      2254       1                  1             0         00b00380     0.0      cyclictest
 0      2256       2                  235           0         00300184     0.0      cyclictest
 0      0          0                  2953543       0         00000000     0.1      IRQ3: [timer]

在这边可以看到cyclictest有两个pid,因为/usr/xenomai/bin/cyclictest它会先创一个线程,并让这个线程跑nanosleep,所以会有两个进程。
接着看向CSW,pid 2254的cyclictest,他的CSW只有1.pid 2256的却有235,这是因为2256是一个xenomai实时任务,而2254是一个linux的进程,所以2256会优先执行,直到实时任务都做完才会换低优先级的linux domain process取得CPU,因此2254的CSW值才会是1而没有增加。

 pi@raspberrypi:~$ sudo kill 2254

 pi@raspberrypi:~$ ps aux | grep -i "cy"
 pi        2324  0.0  0.1   3540   820 ttyAMA0  R+   03:46   0:00 grep --color=auto -i cy
 [1]+  Done                    sudo /usr/xenomai/bin/cyclictest > /dev/null 2> /dev/null

 pi@raspberrypi:~$ sudo /usr/xenomai/bin/cyclictest -p FIFO >/dev/null 2>/dev/null &

在我们了解MSW时,尝试了在-p后面加上了文字(如:FIFO,RR …)

发现MSV的值开始往上增加,也发现一开始对于MSW的定义理解错误,trace后才了解,这是xenomai在-p的指令上是使用atoi,将输入的数字转为int,但并没有进行侦错,才导致段故障,而需跳转到linux domain进行除错。

  CPU    PID        MSW                CSW                PF         STAT          %CPU     NAME
  0      0          0                  75266              0          00500080      99.9     ROOT
  0      2978       1                  1                  0          00b00380      0.0      cyclictest
  0      2980       2                  26846              0          00300184      0.0      cyclictest
  0      7559       1                  1                  0          00b00380      0.0      cyclictest
  0      7561       66                 130                0          00b00184      0.0      cyclictest
  0      0          0                  11266931           0          00000000      0.1      IRQ3: [timer]

2.3 Performance性能表现

Linux实时性对比

  • 原生Linux
   cyclictest -p 90 - m -c 0 -i 200 -n -h 100 -q -l 1000 >log

在这里插入图片描述

  • PREEMPT_RT-patchch Linux
   cyclictest -p 90 - m -c 0 -i 200 -n -h 100 -q -l 1000 >log

在这里插入图片描述

  • Xenomai&Linux
   /usr/xenomai/bin/cyclictest -p 90 - m -c 0 -i 200 -n -v 100 -q -l 100" >log

在这里插入图片描述

user/kernel/timer IRQ (R-pi)

  • Xenomai 3.0
    在这里插入图片描述

  • Xenomai 2.6
    在这里插入图片描述

user/kernel/timer IRQ (Beaglebone)

  • Xenomai 3.0
    在这里插入图片描述

  • Xenomai 2.6
    在这里插入图片描述

2.4 Cyclictest原理

概念:设定一个时间间隔 - >取得现在时间 - >让处理睡一个间隔 - >处理醒来后再取一次时间 - >比对两次取得的时间差与设定的间隔差距

伪代码:

    clock_gettime((&now))
    next = now + par->interval
    while (!shutdown) {
        clock_nanosleep((&next))
        clock_gettime((&now))
        diff = calcdiff(now, next)
        # update stat-> min, max, total latency, cycles
        # update the histogram data
        next += interval
    }

造成时间差的原因:

计时精准度
IRQ延迟
IRQ处理程序持续时间
调度程序延迟
调度程序持续时间

Cyclictest实作流程:

  • 1.cyclictest建立一个timerthread,它一个实时的线程
  • 2.timerthread会重复的执行取第一次时间nanosleep(interval)取第二次时间比对两次时间差与interval的差异
  • 3.最后将结果输出在终端

Clock_nanosleep的计时器

clock_nanosleep使用的计时器是高分辨率计时器(HRT),让睡眠时间可以更精确,达到nanosecond的精准度(但还是要看硬体能提供的精准度)
因为能在更准确得时间让过程醒来并取的nanoscecond单位的时间所以可以计算到由systick无法计算到的持续时间+延迟

Clock_nanosleep实作流程:

  • 1.使用spinlock(xnlock_get_irqsave)令CPU不接受中断
  • 2.使用xnpod_suspend_thread改变目前thread的状态
  • 3.使用xntimer_get_timeout_stopped取得勾选
  • 4.使用ticks2ts转换时间单位
 int clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp)
 {
    xnthread_t *cur;
    spl_t s;
    int err = 0;

    if (xnpod_unblockable_p())
        return EPERM;

    if (clock_id != CLOCK_MONOTONIC && clock_id != CLOCK_REALTIME)
        return ENOTSUP;

    if ((unsigned long)rqtp->tv_nsec >= ONE_BILLION)
        return EINVAL;

    if (flags & ~TIMER_ABSTIME)
        return EINVAL;

    cur = xnpod_current_thread();

    xnlock_get_irqsave(&nklock, s); 

    thread_cancellation_point(cur);

    xnpod_suspend_thread(cur, XNDELAY, ts2ticks_ceil(rqtp) + 1,clock_flag(flags, clock_id), NULL);

    thread_cancellation_point(cur);

    if (xnthread_test_info(cur, XNBREAK)) {

        if (flags == 0 && rmtp) {
            xnsticks_t rem;

            rem = xntimer_get_timeout_stopped(&cur->rtimer);
            xnlock_put_irqrestore(&nklock, s); 

            ticks2ts(rmtp, rem > 1 ? rem : 0); 
        } else
            xnlock_put_irqrestore(&nklock, s); 

        return EINTR;
    } 

    xnlock_put_irqrestore(&nklock, s);

    return err; 
 }

Cyclictest
测试用例:POSIX间隔计时器,间隔500微秒,。100000次循环,100%负载。

命令行:

cyclictest -t1 -p 80 -i 500 -l 100000
  • 使用PREEMPT LINUX
root@raspberrypi:/home/pi# sudo ./cyclictest -t1 -p 80 -i 500 -l 100000
# /dev/cpu_dma_latency set to 0us
policy: fifo: loadavg: 0.00 0.01 0.05 1/61 2064          
T: 0 ( 2063) P:80 I:500 C: 100000 Min:     27 Act:   49 Avg:   42 Max:    1060
  • 使用RT-PREEMPT
Linux raspberrypi 3.6.11+ #474 PREEMPT Thu Jun 13 17:14:42 BST 2013 armv6l GNU/Linux
Min:     22 Act:   31 Avg:   32 Max:     169
  • 使用Xenomai
Linux raspberrypi 3.8.13-core+ #1 Thu Feb 27 03:02:16 CST 2014 armv6l GNU/Linux
Min:      1 Act:    5 Avg:    6 Max:      41

root@raspberrypi:/home/pi# /usr/xenomai/bin/cyclictest -t1 -p 80 -i 500 -l 10000 
0.08 0.06 0.05 1/61 2060          
T: 0 ( 2060) P:80 I:     500 C:  100000 Min:      -4 Act:      -2 Avg:       0 Max:      30

T:Thread
P:优先
I:间隔
C:执行周期数
Min:最小延迟
Act:此次延迟时间
Avg:平均延迟
Max:最大延迟

最重要的是最大值为了确保实时要能知道最坏情况,让开发者可以评估最差的情况可以在多少时间内可以做出回应。

  • 比较Cycifictest于使用PREEMPT LINUX,RT-PREEMPT以及Xenomai。使用R-pi模型B +,Xenoami 2.6.4:
    在这里插入图片描述

3. Xenomai移植

3.1 Installing the Cobalt core

xenomai目录结构

xenomai遵循分离源码模式,将内核态的支持和用户态的库解耦。为此,内核空间和用户空间组件分别位于kernel/lib/子目录下面。其他的顶层文件夹例如scripts/testsuite/utils/,提供在构建主机或运行时目标上使用的其他脚本和程序。

  • kernel/:实现内核内支持代码的代码被视为Linux内核的内置扩展。 因此,应该使用标准的Linux内核配置过程来定义Xenomai内核组件的各种设置。 Xenomai当前引入的所有内核代码都实现了Cobalt内核(即双内核配置)。 到目前为止,Mercury内核在内核空间中不需要Xenomai特定的代码。
  • lib/:包含Xenomai框架导出到应用程序的各种用户空间库。 该树是与内核支持分开构建的。 构建库是为了支持选定的Cobalt或Mercury内核。

Cobalt内核的准备

Xenomai / cobalt提供了无缝集成到Linux的实时扩展内核,因此第一步是将其构建为目标内核的一部分。 为此,scripts / prepare-kernel.sh是一个shell脚本,可以正确设置目标内核。 语法如下:

$ scripts/prepare-kernel.sh [--linux=<linux-srctree>]
[--ipipe=<ipipe-patch>] [--arch=<target-arch>]
  • --linux:指定目标内核源树的路径。 这样的内核树可以已经配置或没有配置,无关紧要。该路径默认为$PWD
  • --ipipe:指定要应用于内核树的中断管道(即I-pipe)补丁的路径。 可从项目的下载区域获得合适的补丁程序。 如果已经打上了I-pipe补丁,则可以省略此参数,否则脚本将建议一个合适的参数。 该脚本将检测内核树中是否已存在中断管道代码,如果存在,则跳过此操作。
  • --arch:告诉脚本有关目标体系结构的信息。 如果未指定,则建议构建主机体系结构为合理的默认设置。

例如,以下命令将准备位于/home/me/linux-3.10-ipipe的Linux树打上Xenomai补丁:

$ cd xenomai-3
$ scripts/prepare-kernel.sh --linux=/home/me/linux-3.10

注意:该脚本将从Xenomai源代码树中自己的位置推断Xenomai内核代码的位置。例如,如果/home/me/xenomai-3/scripts/prepare-kernel.sh正在执行,那么/home/me/xenomai-3/kernel/cobalt中可用的Xenomai内核代码将被修补到目标Linux内核中 。

配置和编译Cobalt内核

准备好之后,即可照常配置目标内核。 所有Xenomai配置选项都可从“ Xenomai”顶级Kconfig菜单中获得。

故障排除指南中记录了几个重要的内核配置选项。

配置完成后,可以照常编译内核。

如果您希望手头有几个不同的配置/构建,则可以通过向每个make调用添加O = … / build- 来重用相同的源。

为了交叉编译Linux内核,请传递ARCH和make命令行上的CROSS_COMPILE变量。 见章节“构建Cobalt/arm内核”,“构建Cobalt/powerpc内核”,“构建Cobalt/Blackfin内核”,“构建Cobalt/x86内核”实例。

Cobalt内核参数

Cobalt内核接受以下参数集,这些参数应由引导加载程序在内核cmdline上传递。

NAME DESCRIPTION DEFAULT
xenomai.allowed_group=< gid > Enable non-root access to Xenomai services from user-space. < gid > is the ID of the Linux user group whose members should be allowed such access by the Cobalt core. None
xenomai.sysheap_size=< kbytes > Set the size of the memory heap used internally by the Cobalt core to allocate runtime objects. This value is expressed in kilo-bytes. 256
xenomai.state=< state > Set the initial state of the Cobalt core at boot up, which may be enabled, stopped or disabled. See the documentation about the corectl(1) utility for a description of these states. enabled
xenomai.clockfreq=< hz-freq > Override the real-time clock frequency used in measuring time intervals with the given value. The most accurate value is normally determined by the Cobalt core automatically when initializing. It is strongly recommended not to use this option unless you really know what you are doing. This value is expressed in HZ. 0 (=calibrated)
xenomai.timerfreq=< hz-freq > Override the real-time timer frequency used in programming timer shots with the given value. The most accurate value is normally determined by the Cobalt core automatically when initializing. It is strongly recommended not to use this option unless you really know what you are doing. This value is expressed in HZ. 0 (=calibrated)
xenomai.smi=< state > x86-specific: Set the state of the SMI workaround. The possible values are disabled, detect and enabled. See the discussion about SMIs for a description of these states. detect
xenomai.smi_mask=< source-mask > x86-specific: Set of bits to mask in the SMI control register. 1 (=global disable)

Building a Cobalt/x86 kernel (32/64bit)

约定:

$linux_tree     path to the target kernel sources
$xenomai_root   path to the Xenomai sources

对于32位和64位平台,为x86构建Xenomai/Cobalt几乎是相同的。但是,您应该注意,无法在x86_64编译的内核上运行x86_32编译的Xenomai库,反之亦然。

  • 假设为x86_64系统,通常应运行:
$ cd $linux_tree
$ $xenomai_root/scripts/prepare-kernel.sh --arch=x86_64 \
  --ipipe=ipipe-core-X.Y.Z-x86-NN.patch
$ make xconfig/gconfig/menuconfig

配置内核(另请参阅此处的建议设置)。

启用Xenomai选项,然后:

$ make bzImage modules
  • 假设为x86 32bit系统,通常应运行:
$ cd $linux_tree
$ $xenomai_root/scripts/prepare-kernel.sh --arch=i386 \
  --ipipe=ipipe-core-X.Y.Z-x86-NN.patch
$ make xconfig/gconfig/menuconfig

配置内核(另请参阅此处的建议设置)。

启用Xenomai选项,然后:

$ make bzImage modules

Building Cobalt/arm kernel

使用名为arm-none-linux-gnueabi-gcc的代码源工具链并为CSB637板(基于AT91RM9200)进行编译,典型的编译如下:

$ cd $linux_tree
$ $xenomai_root/scripts/prepare-kernel.sh --arch=arm \
  --ipipe=ipipe-core-X.Y.Z-x86-NN.patch
$ mkdir -p $build_root/linux
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- O=$build_root/linux \
  csb637_defconfig
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- O=$build_root/linux \
  bzImage modules

Building Cobalt/arm64 kernel

将Linaro工具链与前缀aarch64-linux-gnu-和一起使用为树莓派3板(cortex-a53)进行编译,交叉编译为如下:

$ cd $linux_tree
$ $xenomai_root/scripts/prepare-kernel.sh --arch=arm64 \
   --ipipe=ipipe-core-X.Y.Z-arm64-NN.patch
$ mkdir -p $build_root/linux
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- O=$build_root/linux \
  defconfig
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- O=$build_root/linux \
  Image dtbs modules

3.2 Installing the Mercury core

对于Mercury,到目前为止,除了主机Linux内核已提供的功能外,您还不需要Xenomai特定的内核支持。 您的内核至少应提供高分辨率计时器支持(CONFIG_HIGH_RES_TIMERS),如果您的应用程序需要较短且有限的延迟,则内核可能应完全抢占(PREEMPT_RT)。

也可以使用不具有实时支持的内核,可能用于基本调试任务和/或运行对响应时间没有严格要求的应用程序。

因此,与Cobalt不同,没有其他准备和/或配置Mercury内核的步骤。

3.3 Installing the Xenomai libraries and tools

Prerequisites

Generic requirements (both cores):

  • GCC must have support for legacy atomic builtins (__sync form).

  • GCC必须支持旧式原子内建函数(__sync形式)。

  • GCC should have a (sane/working) support for TLS preferably, although this is not mandatory if building with --disable-tls.

  • 尽管最好使用–disable-tls进行构建,但GCC最好对TLS具有(健全/正常运行的)支持。

  • If you plan to enable the user-space registry support (i.e. --enable-registry), then CONFIG_FUSE_FS must be enabled in the target kernel running the real-time applications. In addition, the FUSE development libraries must be available from the toolchain.

  • 如果计划启用用户空间

  • 支持(即–enable-registry),则必须在运行实时应用程序的目标内核中启用CONFIG_FUSE_FS。 此外,必须从工具链中获得FUSE开发库。

  • If you plan to build from the sources available from the Xenomai GIT tree (git.xenomai.org), the autoconf (>= 2.62), automake and libtool packages must be available on your build system. This is not required when building from a source tree extracted from a release tarball.

  • 如果计划从Xenomai GIT树(git.xenomai.org)的可用资源进行构建,则autoconf(> = 2.62),automake和libtool软件包必须在构建系统上可用。 从从发行版tarball提取的源树进行构建时,这不是必需的。

Cobalt-specific requirements:

  • The kernel version must be 3.10 or better.

  • 内核版本必须为3.10或更高。

  • An interrupt pipeline (I-pipe) patch must be available for your target kernel. You can find the official patches issued by the Xenomai project there. Only patches from the ipipe-core series are appropriate, legacy patches from the adeos-ipipe series are not.

  • 您的目标内核必须有中断管道(I-pipe)补丁。 您可以在此处找到Xenomai项目发布的官方补丁。仅适用ipipe-core系列的补丁,传统adeos-ipipe系列的补丁不适用。

  • A timestamp counter (TSC) is required from running on a x86_32 hardware. Unlike with Xenomai 2.x, TSC-emulation using a PIT register is not available.

  • 在x86_32硬件上运行需要时间戳计数器(TSC)。 与Xenomai 2.x不同,使用PIT寄存器的TSC仿真不可用。

Mercury-specific requirement:

  • There is no particular requirement for Mercury setups, although using a NPTL-based glibc or uClibc is recommended.
  • 尽管建议使用基于NPTL的glibc或uClibc,但对Mercury设置没有特殊要求。

Configuring

如果构建从Xenomai GIT树(git.xenomai.org)获得的源,则必须在Xenomai源树中生成配置脚本和Makefile。 推荐的方法是从源代码树的顶部运行附带的自动重新配置脚本:

$ ./scripts/bootstrap

如果从发行版tarball构建,则可以从提取的源树中轻松获得一组自动配置生成的文件,因此不需要重新配置。

运行时,生成的configure脚本为Cobalt和Mercury内核准备构建库和程序。 内部可能需要的特定于内核的代码在构建时由编译过程自动透明地选择。

可以将下面列出的选项传递给此脚本。

  • 通用的configuration脚本选项:
NAME DESCRIPTION
--with=core=< type > Indicates which real-time core you want to build the support libraries for, namely `cobalt` or `mercury`. This option defaults to `cobalt`.
--prefix=< dir > Specifies the root installation path for libraries, include files, scripts and executables. Running `$ make install` installs these files to `$DESTDIR/ `. This directory defaults to `/usr/xenomai`.
--enable-debug[=partial] This switch controls the debug level. Three levels are available, with varying overhead:

`symbols` enables debug symbols to be compiled in the libraries and executables, still turning on the optimizer (-O2). This option has no overhead, it is useful to get meaningful backtraces using gdb while running the application at nominal speed.
`partial` includes symbols, and also turns on internal consistency checks within the Xenomai code (mostly present in the Copperplate layer). The CONFIG_XENO_DEBUG macro is defined, for both the Xenomai libraries and the applications getting their C compilation flags from the xeno-config script (i.e. xeno-config --cflags). The partial debug mode implicitly turns on --enable-assert. A measurable overhead is introduced by this level. This is the default level when --enable-debug is mentioned with no level specification.
`full` includes partial settings, but the optimizer is disabled (-O0), and even more consistency checks may be performed. In addition to __XENO_DEBUG__, the macro CONFIG_XENO_DEBUG_FULL is defined. This level introduces the most overhead, which may triple the worst-case latency, or even more.

Over the Mercury core, enabling partial or full debug modes also causes the standard malloc interface to be used internally instead of a fast real-time allocator (TLSF). This allows debugging memory-related issues with the help of Valgrind or other dynamic memory analysers.
--disable-debug Fully turns off all consistency checks and assertions, turns on the optimizer and disables debug symbol generation.
--enable-assert A number of debug assertion statements are present into the Xenomai libraries, checking the internal consistency of the runtime system dynamically (see man assert(3)). Passing `--disable-assert` to the configure script disables built-in assertions unconditionally. By default, assertions are enabled in partial or full debug modes, disabled otherwise.
--enable-pshared Enable shared multi-processing. When enabled, this option allows multiple processes to share real-time objects (e.g. tasks, semaphores).
--enable-registry[=/registry-root-path] Xenomai APIs can export their internal state through a pseudo-filesystem, which files may be read to obtain information about the existing real-time objects, such as tasks, semaphores, message queues and so on. This feature is supported by FUSE, which must be available on the target system. Building the Xenomai libraries with the registry support requires the FUSE development libraries to available from the toolchain. In addition, CONFIG_FUSE_FS must be enabled in the target kernel.
Xenomai API可以通过伪文件系统导出其内部状态,可以读取该文件以获取有关现有实时对象(例如任务,信号量,消息队列等)的信息。 FUSE支持此功能,该功能必须在目标系统上可用。 要使用注册表支持构建Xenomai库,需要从工具链中使用FUSE开发库。 另外,必须在目标内核中启用CONFIG_FUSE_FS。

When this option is enabled, the system creates a file hierachy at // under the registry root path, where you can access the internal state of the active real-time objects. The session label is obtained from the --session runtime switch. If no session name is specified, anon@ will be used. E.g. looking at the properties of a VxWorks task could be done as follows:
启用此选项后,系统会在注册表根路径下的`//`中创建文件层次结构,您可以在其中访问活动实时对象的内部状态。 `session`标签是从`--session`运行时开关获取的。 如果未指定session名称,将使用`anon@`。例如查看VxWorks任务的属性可以按照以下步骤进行:
If not specified in the configuration switch, the registry root path will be `/var/run/xenomai`.
$ cat /var/run/xenomai/root/anon@12656/12656/vxworks/tasks/windTask
name       = windTask
errno      = 0
status     = ready
priority   = 70
lock_depth = 0
You may override the default root of the registry hierarchy either statically at build time by passing the desired root path to the --enable-registry configuration switch, or dynamically by using the --registry-root runtime option passed to the application.
When running over Xenomai/cobalt, the /proc/xenomai interface is also available for inspecting the core system state.
--enable-lores-clock Enables support for low resolution clocks. By default, libraries are built with no support for tick-based timing. If you need such support (e.g. for pSOS ™ or VxWorks ™ APIs), then you can turn it on using this option.
The POSIX API does not support tick-based timing. Alchemy may use it optionally.
--enable-clock-monotonic-raw The Xenomai libraries requires a monotonic clock to be available from the underlying POSIX interface. When CLOCK_MONOTONIC_RAW is available on your system, you may want to pass this switch, otherwise CLOCK_MONOTONIC will be used by default.
The Cobalt core implements CLOCK_MONOTONIC_RAW, so this switch is turned on by default when building with `--with-core=cobalt`. On the contrary, this option is turned off by default when building for the Mercury core, since we don’t know in advance whether this feature does exist on the target kernel.
--enable-tls Xenomai can use GCC’s thread local storage extension (TLS) to speed up the retrieval of the per-thread information it uses internally. This switch enables TLS, use the converse --disable-tls to prevent this.
Due to GCC bugs regarding this feature with some release,architecture combinations, whether TLS is turned on by default is a per-architecture decision. Currently, this feature is enabled for x86 and powerpc by default, other architectures will require --enable-tls to be passed to the configure script explicitly.
Unless --enable-dlopen-libs is present, the initial-exec TLS model is selected.
When TLS is disabled, POSIX’s thread-specific data management services are used internally (i.e. pthread_set/getspecific()).
--enable-dlopen-libs This switch allows programs to load Xenomai-based libraries dynamically, using the dlopen(3) routine. Enabling dynamic loading introduces some overhead in TLS accesses when enabled (see --enable-tls), which might be noticeable depending on the architecture.
To support dynamic loading when --enable-tls is turned on, the global-dynamic TLS model is automatically selected.
Dynamic loading of Xenomai-based libraries is disabled by default.
--enable-async-cancel Enables fully asynchronous cancellation of Xenomai threads created by the real-time APIs, making provision to protect the Xenomai implementation code accordingly.
When disabled, Xenomai assumes that threads may exit due to cancellation requests only when they reach cancellation points (like system calls). Asynchronous cancellation is disabled by default.
Fully asynchronous cancellation can easily lead to resource leakage, silent corruption, safety issues and all sorts of rampant bugs. The only reason to turn this feature on would be aimed at cancelling threads which run significantly long, syscall-less busy loops with no explicit exit condition, which should probably be revisited anyway.
--enable-smp Turns on SMP support for Xenomai libraries.
SMP support must be enabled in Xenomai libraries when the client applications are running over a SMP-capable kernel.
--disable-sanity turns off the sanity checks performed at application startup by the Xenomai libraries. This option sets a default, which can later be overriden using the --[no-]sanity options passed to a Copperplate-based Xenomai application. Sanity checks are enabled by default when configuring.
--enable-fortify Enables _FORTIFY_SOURCE when building the Xenomai code unless --enable-debug=full is also given on the command line, in which case --enable-fortify is silently ignored.
--disable-valgrind-client Turns off the Valgrind client support, forcing CONFIG_XENO_VALGRIND_API off in the Xenomai configuration header.
--enable-doc-build Causes the inline Xenomai documentation based on the Doxygen markup language to be produced as PDF and HTML documents. Additional documentation like manpages based on the Asciidoc markup language is produced too.
  • Cobalt-specific configuration options:
NAME DESCRIPTION DEFAULT
--enable-x86-vsyscall Use the x86/vsyscall interface for issuing syscalls. If disabled, the legacy 0x80 vector will be used. Turning on this option requires NPTL. enabled
--enable-arm-tsc Enable ARM TSC emulation. kuser
--enable-arm-quirks Enable quirks for specific ARM SOCs Currently sa1100 and xscale3 are supported. disabled
  • Mercury-specific configuration options:
NAME DESCRIPTION DEFAULT
--enable-condvar-workaround Enable workaround for broken priority inheritance with condition variables in glibc. This option adds some overhead to RTOS API emulators. disabled

Cross-compilation

为了交叉编译Xenomai库和程序,您需要将--host--build选项传递给configure脚本。

  • --host选项允许选择要为其构建库和程序的体系结构。
  • --build选项允许选择运行编译工具的体系结构,即运行配置脚本的系统。

由于交叉编译需要特定的工具,因此此类工具通常以主机体系结构名称为前缀。例如,用于PowerPC体系结构的编译器可以命名为powerpc-linux-gcc

当传递--host = powerpc-linux进行配置时,它将自动使用powerpc-linux-作为所有编译工具名称的前缀,并从该前缀中推断出主机体系结构名称。如果configure无法从交叉编译工具前缀中推断体系结构名称,则必须至少在configure命令行上使用CC和LD变量手动传递所有编译工具的名称。

构建GNU交叉编译器的最简单方法可能涉及使用crosstool-ng(可在此处获得)。

如果要避免构建自己的交叉编译器,则可能会发现更易于使用ELDK。它包括GNU交叉开发工具,例如编译器,binutils,gdb等,以及目标系统上所需的许多预先构建的目标工具和库。有关更多详细信息,请参见此处。

其他一些预先构建的工具链:

  • Mentor Sourcery CodeBench Lite Edition,可在此处获取;
  • Linaro工具链(用于ARM体系结构),可在此处获得。

Building the x86 libraries (32/64bit)

约定:

$xenomai_root   path to the Xenomai sources
$build_root     path to a clean build directory
$staging_dir    path to a directory that will hold the installed file temporarily before they are moved to their final location; when used in a cross-compilation setup, it is usually a NFS mount point from the target’s root directory to the local build host, as a consequence of which running make{nbsp}DESTDIR=$staging_dir{nbsp}install on the host immediately updates the target system with the installed programs and libraries.

Assuming that you want to build the Mercury libraries natively for a x86_64/SMP system, enabling shared multi-processing support. You would typically run:

$ mkdir $build_root && cd $build_root
$ $xenomai_root/configure --with-core=mercury --enable-smp --enable-pshared
$ make install

Conversely, cross-building the Cobalt libraries from x86_64 with the same feature set, for running on x86_32 could be:

$ mkdir $build_root && cd $build_root
$ $xenomai_root/configure --with-core=cobalt --enable-smp --enable-pshared \
  --host=i686-linux CFLAGS="-m32 -O2" LDFLAGS="-m32"
$ make install

安装构建树后(即使用“ make install”),应在安装根目录中填充可用于构建基于Xenomai的实时应用程序的库,程序和头文件。 该目录路径默认为/usr/xenomai

其余示例说明了如何针对各种体系结构交叉编译Xenomai。当然,您必须首先为目标系统安装正确的交叉编译工具链。

Building the ARM libraries

Using codesourcery toolchain named arm-none-linux-gnueabi-gcc and compiling for a CSB637 board (AT91RM9200 based), a typical cross-compilation from a x86_32 desktop would look like:

$ mkdir $build_root/xenomai && cd $build_root/xenomai
$ $xenomai_root/configure CFLAGS="-march=armv4t" LDFLAGS="-march=armv4t" \
  --build=i686-pc-linux-gnu --host=arm-none-linux-gnueabi- --with-core=cobalt
$ make DESTDIR=$staging_dir install

与以前的版本不同,Xenomai不再将任何特定于ARM架构的标志或FPU标志传递给gcc,因此,如上所示,用户应使用CFLAGS和LDFLAGS变量传递它们,其中AT91RM9200基于ARM920T内核,实现了 armv4体系结构。 下表总结了CFLAGS和选项,它们在以前的版本中自动传递,现在需要明确传递以进行配置,以支持受支持的SOC:

SOCCFLAGSconfigure options
at91rm9200-march=armv4t -msoft-float-
at91sam9x-march=armv5 -msoft-float-
imx1-march=armv4t -msoft-float-
imx21-march=armv5 -msoft-float-
imx31-march=armv6 -mfpu=vfp-
imx51/imx53-march=armv7-a -mfpu=vfp3-
imx6q-march=armv7-a -mfpu=vfp3–enable-smp
ixp4xx-march=armv5 -msoft-float–enable-arm-tsc=ixp4xx
omap3-march=armv7-a -mfpu=vfp3-
omap4-march=armv7-a -mfpu=vfp3–enable-smp
orion-march=armv5 -mfpu=vfp-
pxa-march=armv5 -msoft-float-
pxa3xx-march=armv5 -msoft-float–enable-arm-quirks=xscale3
s3c24xx-march=armv4t -msoft-float-
sa1100-march=armv4t -msoft-float–enable-arm-quirks=sa1100

如果您的工具链不支持目标体系结构,则可以针对较旧的体系结构版本(v6而不是v7或v4而不是v5)进行构建,唯一的限制是如果启用了SMP,则该体系结构不应小于 v6。

Building the ARM64 libraries

ARM64 is only supported from the git repos on the next branch.

Using the Linaro toolchain with the prefix aarch64-linux-gnu- for the Raspberry Pi 3 board (cortex-a53), cross compililation from a x86_64 host would be as follows:

mkdir $build_root/xenomai && cd $build_root/xenomai
../xenomai/configure CFLAGS="-mtune=cortex-a53" LDFLAGS="-mtune=cortex-a53" \
--build=i686-pc-linux-gnu --host=aarch64-linux-gnu --with-core=cobalt \
--enable-smp CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld
$ make DESTDIR=$staging_dir install

Passing a value for the -mcpu flag will help generate optimized code for a specific cpu type but it’s not necessary.

Testing the installation

  • Booting the Cobalt kernel

In order to test the Xenomai installation over Cobalt, you should first try to boot the patched kernel. Check the kernel boot log for messages like these:

$ dmesg | grep -i xenomai
I-pipe: head domain Xenomai registered.
[Xenomai] Cobalt vX.Y.Z enabled

If the kernel fails booting, or the log messages indicates an error status instead, see the Troubleshooting guide.

  • Testing the real-time system (both cores)

First, run the latency test:

$ /usr/xenomai/bin/latency

The latency test should display a message every second with minimum, maximum and average latency values. If this test displays an error message, hangs, or displays unexpected values, see the Troubleshooting guide.

If the latency test succeeds, you should try next to run the xeno-test test in order to assess the worst-case latency of your system. Try:

$ xeno-test --help
  • Calibrating the Cobalt core timer

The accuracy of the Cobalt timing services depends on proper calibration of its core timer. Sound factory-default calibration values are defined for each platform Xenomai supports, but it is recommended to calibrate the core timer specifically for the target system.

See the documentation about the autotune(1) utility.

3.3 Building and running Xenomai 3 applications

延迟测试在目标系统上按预期方式运行后,就可以运行实时应用程序了。

您可能需要查看本文档,以获取有关应用程序构建过程的详细信息。

另外,您可以参考本文档以了解Xenomai 3应用程序可用的命令行选项。

Compiling a Xenomai 3 application

总结起来,您应该使用xeno-config脚本来获取与Xenomai相关的正确编译和链接器标志,以便为任何Cobalt或Mercury内核构建应用程序。

可在此URL上找到xeno-config脚本的完整用法。

下面是一个简单的Makefile片段,用来构建使用VxWorks仿真API的单文件应用程序vxapp.c:

XENO_CONFIG := /usr/xenomai/bin/xeno-config
CFLAGS := $(shell $(XENO_CONFIG) --vxworks --cflags)
LDFLAGS := $(shell $(XENO_CONFIG) --vxworks --ldflags)
CC := $(shell $(XENO_CONFIG) --cc)

EXECUTABLE := vxapp

all: $(EXECUTABLE)

%: %.c
	$(CC) -o $@ $< $(CFLAGS) $(LDFLAGS)

Compiling a RTDM-based module

构建常规内核模块/驱动程序的规则也适用于基于RTDM的驱动程序,没有其他要求。 例如,基于RTDM API,由some.driver和bar.c这两个文件组成的用于构建some_driver.ko的Makefile为:

obj-y += some_driver.o
some_driver-y := foo.o bar.o

应该从包含模块源的目录中在内核树之外构建此模块,如下所示:

$ make -C /path/to/kernel/tree M=$PWD modules

3.4 Running a Xenomai 3 application

对于Cobalt,您将需要如本文档中所述将实时内核内置到目标Linux内核中。

对于Mercury,到目前为止,除了主机Linux内核已提供的功能外,您还不需要Xenomai特定的内核支持。您的内核至少应提供高分辨率计时器支持(CONFIG_HIGH_RES_TIMERS),如果您的应用程序需要较短且有限的延迟,则内核可能应完全抢占(PREEMPT_RT)。

任何基于Xenomai的应用程序都可以识别可能在命令行中传递的一组标准选项,如本文档所述。

此外,在Xenomai内核上运行的Alchemy,pSOS™和VxWorks™API可以定义要使用的时钟分辨率,以纳秒为单位HZ =(1000000000 / ns)。即-{alchemy/psos/vxworks}-clock-resolution = <ns>选项。

如果您的应用程序结合了多个API,则可以传递多个时钟分辨率开关来设置它们。

默认值取决于所考虑的API。例如,VxWorks™和pSOS™仿真器默认为毫秒时钟速率。默认情况下,Alchemy API是无滴答的,即--alchemy-clock-resolution = 1

指定大于1纳秒的分辨率要求Xenomai库提供低分辨率时钟支持(请参阅–enable-lores-clock配置开关)。

Valgrind support

目前仅在Mercury核心上可以使用通过Valgrind运行Xenomai应用程序。

当Valgrind API可用于应用程序过程时,配置符号CONFIG_XENO_VALGRIND_API是在构建时定义的,并且可以由应用程序代码测试是否存在。 请参阅此地址的工具文档。

Xenomai autoconf脚本将自动检测构建系统上的Valgrind核心标头,并相应地定义此符号(即/usr/include/valgrind/valgrind.h)。

您可能需要在构建系统上安装Valgrind开发包,以提供核心头文件。 例如,这种软件包在Fedora上称为valgrind-devel。

3.5 Xenomai Application

除非另外说明,否则该描述无异地适用于Cobalt和Mercury的构型。 读者可以假定应用程序总是引用与Xenomai库链接的任何可执行程序,无论其目的如何。

此讨论专门涉及基于Xenomai库的构成应用程序的用户空间程序。 没有任何参考关于任何基于内核的上下文。

How to build an application

最重要的经验法则是始终分别从对xeno-config --cflagsxeno-config --ldflags的调用中检索编译和链接标志,并提及应用程序使用的Xenomai API列表。 可在此URL上找到xeno-config脚本的完整用法。

从Makefile调用xeno-config:

CFLAGS := $(shell xeno-config --posix --cflags)
LDFLAGS := $(shell xeno-config --posix --ldflags)

如果不使用xeno-config来获取Xenomai应用程序的正确构建标志,将产生一个错误的可执行文件,这根本行不通。

Standard Xenomai command line options

与Xenomai库链接的任何应用程序都会自动识别可以在命令行上传递的一组标准选项,这些选项适用于任何基于Xenomai的程序。 您无需在应用程序代码中添加任何内容即可解释和执行这些选项开关。 例如,--dump-config选项始终被应用程序识别,转储用于构建与其链接的Xenomai库的设置:

Xenomai/mercury v3.0-rc5 -- #e91216a (2015-05-12 17:58:01 +0200)
CONFIG_MMU=1
CONFIG_SMP=1
CONFIG_XENO_ASYNC_CANCEL=1
CONFIG_XENO_BUILD_ARGS=" '-prefix=/home/rpm/install/xenomai/mercury' '--enable-async-cancel' '--enable-lores-clock' '--enable-debug=full' '--enable-smp' '--enable-tls' '--with-core=mercury' '--enable-registry' '--enable-pshared' '--enable-maintainer-mode'"
CONFIG_XENO_BUILD_STRING="x86_64-unknown-linux-gnu"
CONFIG_XENO_COMPILER="gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) "
CONFIG_XENO_DEBUG=1
CONFIG_XENO_DEBUG_FULL=1
CONFIG_XENO_DEFAULT_PERIOD=100000
CONFIG_XENO_FORTIFY=1
...
CONFIG_XENO_LIBS_DLOPEN is OFF
CONFIG_XENO_LORES_CLOCK_DISABLED is OFF
CONFIG_XENO_RAW_CLOCK_ENABLED is OFF
CONFIG_XENO_TLSF is OFF
CONFIG_XENO_WORKAROUND_CONDVAR_PI is OFF

标准的选项列表:

NAME DESCRIPTION
--registry-root=< path > Tells Xenomai to root the object registry at the given path, instead of the default root (see the `--enable-registry` switch from the configuration options).
--session=< label > Name of the session the new process will be part of (or create if not present). If `--enable-pshared` was given when configuring the Xenomai libraries, this label allows multiple processes giving the same label at startup to share objects created by members of the same session.
All shared objects are allocated from a global heap backed by the main memory pool (see --mem-pool-size).
--shared-registry Exports the registry of the process to other users. If access is possible, also depends on permissions of the registry path.
By default, the registry is only accessible for the user that started the Xenomai process.
--no-registry This switch disables registry support at runtime. No real-time objects will be exported to the registry, despite the registry code was compiled in.
--mem-pool-size=< size[K|M|G] > The size the main memory pool backing the session’s heap, which can be suffixed by a K(ilobyte), M(egabyte) or G(igabyte) binary multiplier. Most of the dynamic allocation requests issued from the Xenomai libraries are served from this pool. In absence of suffix, the value is normally interpreted as a count of bytes, except if lower than 65536, see below.
支持会话堆的主内存池的大小,可以以K(ilobyte),M(egabyte)或G(igabyte)二进制乘数作为后缀。 Xenomai库发出的大多数动态分配请求都从该池中获得。 在没有后缀的情况下,该值通常解释为字节数,除非低于65536,请参见下文。
If the value is lower than 65536 with no suffix, it is
interpreted as a count of kilobytes for backward compatibility
purpose with the former syntax. This work around may disappear
anytime when transitioning to Xenomai 3.0 final, so make sure
to fix any launch script(s) accordingly. Typically, suffixing
the current value with a 'K' multiplier would address the issue.
The shared heap is living in the /tmpfs filesystem, and therefore consumes RAM space.
--cpu-affinity=< cpu[,cpu]… ​> The set of CPUs available for running Xenomai threads created by the application, in absence of explicit pinning of such threads to a particular CPU when they are spawned. Defaults to any online CPU.
The argument is a list of valid (i.e. online) CPU numbers separated by commas. This option may appear several times on the command line, cumulating the settings.
--main-prio=< priority > Cobalt only. When xenomai_init() is called for bootstrapping the real-time services for the current process, the calling context is automatically turned into a Xenomai thread, managed by the Cobalt kernel. Normally, this call runs over the main() routine, hence the parameter name. By default, the current scheduling policy and priority are kept during the transition. + This parameter allows to force new scheduling parameters for the thread invoking xenomai_init() when it enters the Xenomai realm, according to the following rules:
if is zero, the calling thread will be assigned the SCHED_OTHER policy.
if is greater than zero, the calling thread will be assigned the SCHED_FIFO policy, and the given priority level.
if is negative, the scheduling parameters of the calling context will be kept unchanged (default case).

仅Cobalt。 当调用xenomai_init()引导当前进程的实时服务时, 调用上下文将自动转换为由Cobalt内核管理的Xenomai线程。 通常,此调用在main()例程上运行,因此在参数名称上运行。 默认情况下,当前的调度策略和优先级在过渡期间保留。 +根据以下规则,此参数允许为进入xenomai领域的xenomai_init()线程强制新的调度参数:
如果为零,则将为调用线程分配SCHED_OTHER策略。
如果大于零,则将为调用线程分配SCHED_FIFO策略和给定的优先级。
如果为负,则调用上下文的调度参数将保持不变(默认情况)。
--print-buffer-size=< num-bytes > Cobalt only. When symbol wrapping is in effect (default case for applications based on Cobalt’s POSIX API), Xenomai interposes on common output calls from the stdio support such as printf(3) fprintf(3) and puts(3), so that no delay or loss of real-time guarantee is incurred by the caller.
The underlying mechanism is based on relay buffers forming an output ring, filled in by real-time threads locklessly, which are periodically flushed by a regular (non real-time) Linux helper thread to the process’s destination stream (stdout, stderr or any other particular stream). + This parameter allows to set the size of a typical relay buffer. By default, a relay buffer is 16k large.

仅钴。 当符号换行有效时(基于Cobalt的POSIX API的应用程序的默认情况),Xenomai插入来自stdio支持的常见输出调用,例如printf(3)fprintf(3)和puts(3),这样就不会造成延迟或丢失 呼叫者需要支付实时保证金。
底层机制基于形成输出环的中继缓冲区,这些缓冲区由实时线程无锁地填充,并由常规(非实时)Linux帮助程序线程定期将其刷新到进程的目标流(stdout,stderr或任何其他对象) 特定的流)。 +此参数允许设置典型中继缓冲区的大小。 默认情况下,中继缓冲区为16k。
--print-buffer-count=< num-buffers > Cobalt only. Use this parameter to set the total number of relay buffers (see --print-buffer-size). By default, 4 relay buffers are present in the output ring.
--print-sync-delay=< ms > Use this parameter to set the longest delay (in milliseconds) before the output pending into the relay buffers is synchronized (see --print-buffer-size). By default, the output ring is flushed each 100 milliseconds by the helper thread.
--[no-]sanity Turn on[/off] sanity checks performed by the Xenomai libraries, mostly during the application startup. Defaults to the value set by the --enable-sanity switch when configuring (which is enabled by default).
For instance, running Xenomai libraries built for a uni-processor system over a SMP kernel is detected by such checks.
--verbose[=level] Set verbosity to the desired level, or 1 if unspecified. The level argument may be interpreted differently depending on the application, however --verbose=0 must mean fully quiet. The interpretation of higher levels is application-specific. Defaults to 1.
--silent
--quiet
Same as --verbose=0.
--trace[=level] Set tracing to the desired level, or 1 if unspecified. The level argument may be interpreted differently depending on the application, however --trace=0 must disable tracing. Level 1 allows tracing the Xenomai library bootstrap code. The interpretation of higher levels is application-specific. Defaults to 0.
--version Get the application and Xenomai version stamps. The program immediately exits with a success code afterwards.
--dump-config Dump the settings used for building the Xenomai libraries the application is linked against.
--no-mlock Mercury only. This switch disables the implicit mlock() call at init, normally used for locking the calling process’s virtual address space into RAM, in order to avoid the extra latency induced by virtual memory paging.
This option does not apply to the Cobalt core, which requires
memory locking from all clients unconditionally.
--help Display the help strings.

Accessing the tunables

  • Tunables:可调项是在设置系统(例如主内存堆的大小)或控制其运行时行为(例如详细程度)时用作配置值的变量。

  • configuration tunables:其中的一些可调参数可能会由应用程序的早期代码更新,直到系统开始初始化核心数据结构并启动服务,然后从该点开始变为只读状态。 这些称为配置可调参数。

  • runtime tunables:在应用程序生命周期的任何时间点,其他可调参数都可以自由更改。 这些称为运行时可调参数。

有一个简单的API,用于读取和更新任何可调参数的当前值,该参数由{get/set}_config_tunable(name){get/set}_runtime_tunable(name)C宏组成。

--enable-assert对于Xenomai库有效时,set_config_tunable()在配置调整阶段之后通过在尝试更改此只读可调参数会引发异常。

每个可调参数都有一个任意的名称,它不一定反映实际存储其值的变量的名称。 例如,可以定义可调foo,最终在后台处理int bar C变量,甚至是一组相关变量。

通常,更新和读取verbose_level可调参数,该参数用来保留应用程序的当前详细程度-如下所示:

#include <stdio.h>
#include <xenomai/tunables.h>

set_runtime_tunable(verbosity_level, 1);
printf("verbosity_level=%d\n", get_runtime_tunable(verbosity_level));

当配置可调参数与更新相同变量的命令行更新相同的变量,命令行优先于set_config_tunable()调用。

Xenomai库定义了以下配置可调参数(configuration tunables):

NAMEDESCRIPTIONDEFAULT
cpu_affinitysame as --cpu-affinity optionany online CPU
no_mlocksame as --no-mlock optionoff
no_sanitysame as --no-sanity option!CONFIG_XENO_SANITY
no_registrysame as --no-registry optionoff (i.e. enabled)
mem_pool_sizesame as --mem-pool-size option1Mb
session_labelsame as --session optionnone (i.e. anonymous)
registry_rootsame as --registry-root optionCONFIG_XENO_REGISTRY_ROOT
shared_registrysame as --shared-registry optionoff (i.e. private)

Xenomai库定义了以下运行时可调参数(runtime tunables):

NAMEDESCRIPTIONDEFAULT
verbosity_levelsame as --verbose option1
trace_levelsame as --trace option0

您可以使用define_{config/runtime}_tunable(name)read_{config/runtime}_tunable(name)C宏添加自己的可调参数。

define_tunable语法提供了用于更新与可调名称匹配的C值的帮助程序代码。 相反,read_tunable语法提供了用于返回与可调名称匹配的C值的帮助程序代码。

应用程序代码应使用{get/set}_config_tunable(name){get/set}_runtime_tunable(name)来访问新的可调参数,具体取决于其范围。

定义和使用简单的可调参数:

/* Out of line definition. */

code.c:

int foo_runtime_variable;

define_runtime_tunable(foo, int, val)
{
	/*
	 * The code to update the internal variable upon a call to
	 * set_runtime_tunable(foo).
	 */
	foo_runtime_variable = val;
}

read_runtime_tunable(foo, int)
{
	/*
	 * The code to return the current tunable value upon a
	 * call to get_runtime_tunable(foo).
	 */
	return foo_runtime_variable;
}

header.h:

/* Declaration of the manipulation helpers */

define_runtime_tunable(foo, int, val);
read_runtime_tunable(foo, int);

/* Conversely, we could be using an inline definition */

header.h:

extern int foo_runtime_variable;

static inline define_runtime_tunable(foo, int, val)
{
	foo_runtime_variable = val;
}

static inline read_runtime_tunable(foo, int)
{
	return foo_runtime_variable;
}

/* Accessing the new tunable, inverting the value. */

int setting = get_runtime_tunable(foo);
set_runtime_tunable(foo, !setting);

在考虑将它们用于初始化应用程序之前,为某些配置可调参数提供自己的出厂默认值可能会很方便。

为了使这种提早进行调整,您需要在Xenomai引导程序代码运行的调整处理程序链中插入自己的处理程序,然后再开始实际的初始化过程。 可以使用Xenomai设置描述符完成此操作,如下所示:

#include <xenomai/init.h>
#include <xenomai/tunables.h>

static int foo_tune(void)
{
	/*
	 * We need more than 1MB which is Xenomai's default, make
	 * it 16MB before the Xenomai core starts initializing
	 * the whole thing. --mem-pool-size=<size> may override
	 * this value.
	 */
	set_config_tunable(mem_pool_size, 16MB);

	/*
	 * Also turn verbosity off by default. --verbose=<level> on the
	 * command line may switch this to verbose mode.
	 */
	 set_runtime_tunable(verbosity_level, 0);

	 return 0; /* Success, otherwise -errno */
}

/*
 * CAUTION: we assume that all omitted handlers are zeroed
 * due to the static storage class. Make sure to initialize them
 * explicitly to NULL if the descriptor belongs to the .data
 * section instead.
 */
static struct setup_descriptor foo_setup = {
	.name = "foo",
	.tune = foo_tune,
};

/* Register the setup descriptor. */
user_setup_call(foo_setup);

Application entry (C/C++)

从用户代码的角度来看,每个应用程序都照常在main()例程中启动。

一些用于构建应用程序的链接标志会影响其初始化顺序。 通过调用xeno-config --ldflags检索此类标志,并传递应用程序依赖的Xenomai API列表(例如xeno-config --posix --alchemy --ldflags)。

Xenomai库可以妥善处理此类序列以适当地引导其服务(即自动引导automatic bootstrap),或将此类职责留给应用程序代码(即手动引导manual bootstrap)。

  • Automatic bootstrap

如果使用自动引导程序模式,则应用程序在main()函数中接收命令行中非标准xenomai选项的其他选项。 在这种模式下,输入main()后所有Xenomai服务都将立即可用。

这是大多数应用程序应使用的默认行为。

  • Manual bootstrap

在手动引导模式下,应用程序将接收未更改的原始参数向量,而Xenomai服务未初始化。如果需要在Xenomai服务启动之前进行一些自己的早期初始化,则应使用此模式。

通过链接从xeno-config --ldflags --no-auto-init接收到的标志来启用手动引导模式。

在这种模式下,应用程序代码必须在初始化步骤中的某个时候调用xenomai_init(&argc,&argv),以引导该进程的Xenomai服务。此函数处理所有标准Xenomai选项,然后更新参数以指向从Xenomai选项中删除的原始向量的副本。

之后,应用程序代码可以从命令行收集和处理未处理的选项,并且可以不受限制地使用Xenomai服务。

  • Dealing with C++ static constructors

初始化顺序保证了在主要可执行文件中实例化的对象的C++构造函数(具有默认优先级)将在Xenomai服务以自动模式引导后运行。这意味着来自主要可执行文件的静态对象的类构造函数可以调用Xenomai服务。

由于在手动引导方式下调用xenomai_init()之前Xenomai服务不可用,因此在这种情况下,直到调用该例程并成功返回之前,这种保证不存在。

如通过各个软件层进行的初始化过程所述,由共享库实例化的静态对象的C++构造函数将无法访问Xenomai服务,因为Xenomai引导程序代码稍后运行。

如果您具有非默认的静态构造函数优先级,或在可执行文件所依赖的共享库中实例化的静态对象,则可能需要提供自己的早期引导程序代码,以防构造函数需要调用Xenomai服务。在这些C++构造函数调用Xenomai服务之前,此代码最终应调用xenomai_init()引导Xenomai服务。

  • Handling application-defined arguments

您可能需要提供自己的命令行选项,以供应用程序代码解释。

如果使用自动引导程序模式,则应用程序在main()函数中接收调用时传递的命令行选项集,该命令行开关是从Xenomai标准选项中删除的。

使用手动引导程序,应用程序将接收在命令行上传递的原始参数向量。然后,调用xenomai_init(&argc,&argv)进程会从向量中删除所有标准Xenomai选项,然后再返回到调用方。

在这两种情况下,可以照常使用getopt(3)解析器检索参数向量中存在的特定于应用程序的选项。

解析特定于应用程序的参数:

#include <getopt.h>
#include <stdio.h>
#include <xenomai/init.h>

int foo_mode = 1; /* Default */

static const struct option options[] = {
	{
#define foo_mode1_option	0
		.name = "foo1",
		.flag = &foo,
		.val = 1,
	},
	{
#define foo_mode2_option	1
		.name = "foo2",
		.flag = &foo,
		.val = 2,
	},
	{
		/* sentinel */
	}
};

int main(int argc, char *const *argv)
{
	int lindex, ret, opt;

#ifdef MANUAL_BOOTSTRAP
        /* xeno-config --no-auto-init --ldflags was given. */
        ret = xenomai_init(&argc, &argc);
#endif
	for (;;) {
		lindex = -1;
		opt = getopt_long_only(argc, argv, "", options, &lindex);
		if (opt == EOF)
			break;
		switch (lindex) {
		case foo_mode1_option:
		case foo_mode2_option:
		     break;
		default:
			usage();
			return 1;
		}
	}
	...
}

Under the hood

Xenomai特定的应用程序初始化过程按如下所示引导软件层: app-init-layers

lib/boilerplate/init/bootstrap.c实现了bootstrap处理程序,该处理程序作为目标文件粘贴到应用程序的主要可执行文件中,因此可以确保在应用程序依赖的任何共享库构造函数之后运行,而无论库构造函数如何优先事项。

在自动引导程序模式下,xenomai_main()例程插入实际的main()入口点,以将经过处理的自变量矢量(从Xenomai标准选项中删除)传递给它。 此技巧基于链接器的符号换行功能(–wrap)。

xeno-config发出正确的链接器标志集以启用这种插入,除非给出–no-auto-init。

  • Adding your own setup code

Xenomai代表了粘在应用程序上的引导程序处理程序,它实现了一种灵活的机制来有序地运行安装程序代码。

此机制基于安装程序描述符(结构setup_descriptor),每个描述符定义一组可选的处理程序,这些应由Xenomai引导程序代码在初始化过程的不同阶段调用。为每个描述符分配一个优先级,该优先级定义了实现同一处理程序的多个描述符之间的调用顺序。相应的C定义和类型应通过包含<xenomai/init.h>获得。

共有三个初始化阶段,按优先顺序列出,每个阶段在设置描述符中分配了一个处理程序。 NULL处理程序可用于跳过给定阶段的描述符。

通过setup_descriptor→tune()进行工厂调整步骤。每个设置描述符都有机会为标准或应用程序定义的可调参数分配值。这是我们用于更改默认可调值的处理程序。

通过setup_descriptor→parse_option()进行命令行选项解析。如果存在此处理程序,则setup_descriptor→options必须指向getopt(3)选项数组,枚举此描述符定义的有效选项。

→parse_option()处理程序将找到的每个选项的位置传递给命令行,该位置与setup_descriptor→options数组中的条目以及接收到的参数值匹配。如果该选项不包含任何参数,则将传递NULL。

每当在命令行上传递–help开关时,都会调用→help()处理函数。从低优先级到高优先级将依次调用描述符链中的所有帮助处理程序。

选项解析在调整步骤之后进行,因此影响可调参数的命令行选项可以覆盖→tune()处理程序定义的出厂值。

通过setup_descriptor→init()正确执行初始化步骤。根据从先前步骤获得的当前设置,调用此处理程序以调出其提供的功能或服务。

如果成功,所有处理程序应返回零,如果出错,则返回否定的POSIX错误代码。一旦出错,引导程序序列将立即终止,并且应用程序将退出并显示一条致命消息。

下面的示例在初始化序列中引入了自定义代码,用于设置一些基本可调参数,然后在启用特定功能之前解析一组本地命令行选项。在触发了同一类别的所有Xenomai处理程序之后,将调用这些自定义处理程序。例如,此类代码可以是附加到您的应用程序的辅助库的一部分,或由您的主要可执行文件实现。

添加自定义设置代码

3.6 AM528移植

1、ipipe补丁:

cd Scara_kernel
patch < ../ipipe-core-4.4.71-arm-9.patch

根据.rej文件,解决掉patch中的冲突部分。

2、xenomai补丁:

生成xenomai补丁文件:

cd xenomai-3.0.10
./scripts/prepare-kernel.sh --linux=/home/myc/vx/xenomai/Scara_kernel/ --arch=arm --outpatch=../xenomai-3.0.10.patch

对kernel打上补丁:

cd Scara_kernel
patch -p1 < ../xenomai-3.0.10.patch

3、编译内核:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- tisdk_am57xx-evm_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am57xx-evm-reva3.dtb
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j4

4、编译用户态库:

./configure CFLAGS="-march=armv7-a -mtune=cortex-a15 -mfloat-abi=hard -mfpu=neon -ffast-math" --host=arm-linux-gnueabihf --with-core=cobalt --enable-smp --enable-pshared

make DESTDIR=/home/myc/vx/xenomai/build_fs install

5、编译应用:

6、测试:

root@am57xx-evm:~# latency 
== Sampling period: 1000 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT|  00:00:01  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|      0.346|      1.050|      6.007|       0|     0|      0.346|      6.007
RTD|      0.319|      0.984|      8.974|       0|     0|      0.319|      8.974
RTD|      0.293|      1.060|      4.826|       0|     0|      0.293|      8.974
RTD|      0.427|      1.060|      4.907|       0|     0|      0.293|      8.974
RTD|      0.263|      0.857|      4.336|       0|     0|      0.263|      8.974
RTD|      0.145|      0.812|      5.591|       0|     0|      0.145|      8.974
RTD|      0.303|      0.845|      6.775|       0|     0|      0.145|      8.974
RTD|      0.254|      0.869|      6.612|       0|     0|      0.145|      8.974
RTD|      0.112|      0.773|      4.904|       0|     0|      0.112|      8.974
RTD|      0.246|      0.810|      6.530|       0|     0|      0.112|      8.974
RTD|      0.405|      0.915|      5.487|       0|     0|      0.112|      8.974
RTD|      0.239|      0.847|      6.692|       0|     0|      0.112|      8.974
RTD|      0.261|      0.825|      4.577|       0|     0|      0.112|      8.974
RTD|      0.397|      0.920|      6.609|       0|     0|      0.112|      8.974
RTD|      0.254|      0.792|      4.627|       0|     0|      0.112|      8.974
RTD|      0.414|      0.939|      4.738|       0|     0|      0.112|      8.974
RTD|      0.249|      0.862|      4.575|       0|     0|      0.112|      8.974
RTD|      0.246|      0.821|      5.607|       0|     0|      0.112|      8.974
RTD|      0.290|      0.955|      5.037|       0|     0|      0.112|      8.974
RTD|      0.238|      0.832|      4.410|       0|     0|      0.112|      8.974
RTD|      0.259|      0.821|      4.458|       0|     0|      0.112|      8.974
RTT|  00:00:22  (periodic user-mode task, 1000 us period, priority 99)

3.7 ubuntu移植

1、ipipe补丁:

cd linux-4.14.134
patch -p1 < ../ipipe-core-4.19.89-x86-9.patch

2、xenomai补丁:

生成xenomai补丁:

cd xenomai-3.0.10
./scripts/prepare-kernel.sh --linux=/home/pwl/xenomai/linux-4.19.99 --arch=x86_64 --outpatch=/home/pwl/xenomai/xenomai-3.0.10.patch

打上xenomai补丁:

cd linux-4.14.134
patch -p1 < ../xenomai-3.0.10.patch

3、编译内核

pwl@pwl-T5:~/xenomai/linux-4.14.134$ make menuconfig
scripts/kconfig/mconf  Kconfig
warning: (X86_VSMP && HYPERV) selects PARAVIRT which has unmet direct dependencies (HYPERVISOR_GUEST && !IPIPE)

注意去掉和IPIPE冲突的X86_VSMP && HYPERV特性。

make menuconfig
make bzImage modules

4、安装内核

make INSTALL_MOD_STRIP=1 modules_install
sudo mkinitramfs /lib/modules/4.14.134+ -o /boot/initrd.img-4.14.134-xenomai
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-4.14.134-xenomai
sudo cp System.map /boot/System.map-4.14.134-xenomai
sudo update-grub2

5、编译用户态库并安装

cd xenomai-3.0.10
./configure --with-core=cobalt --enable-smp --enable-pshared
make DESTDIR=/home/myc/vx/xenomai/build_fs install
sudo make install

6、测试:

Logo

鸿蒙生态一站式服务平台。

更多推荐