xenomai初探

Xenomai定义

一个real time的系统需要保证其工作在给定的时间限制之内完成。系统不需要以最快的速度完成任务,但是需要在指定的定时时间范围内完成。
在这个前提条件下,realtime的系统任务完成时间是可确定的,根据运行的底层系统的不同,可以分为以下两类实时系统:

  • Soft Real Time:
    系统不一定每次都需要遵守deadline,但较多的deadline miss会导致服务质量降低。
  • Hard Real Time:
    系统能每次都能在deadline内完成任务。

Real time on Linux

  • PREEMPT_RT (in-kernel; single kernel)
    修改原本的 GNU/Linux 核心 (vanilla kernel),以减少non-preemptible section的方式,使其逐步改善 real-time 能力。

  • dual kernel (eg: RTLinux, RTAI, Xenomai)
    运作一个 real-time 核心,然后将修改后的的 GNU/Linux 核心程式视为 real-time 核心的 idle task。

在xenomai中,dual kernel 就是 Xenomai 的 Nucleus / Cobalt Core 和 Linux kernel。Xenomai 改变整个系统架构,让 ipipe -> xenomai scheduler 来预先处理 real-time task,而 Linux 则拉到上层成为一个task。这样可以避免 Linux 因为庞大的架构而影响处理 real-time 的时间。

Xenomai系统架构

image
Xenomai是一个linux kernel的patch 借由在底层增加一个架构 负责硬体与接收interrupt 并将interrupt 传给上层的OS(这边称为domain)
这个底层的架构是Adeos 是另一个open source的project
在api呼叫上可以看到不同层级的抽象化
ipipe_XXX -> rthal_XXX -> xnXXX
负责传送interrupt的程式称为ipipe 示意图 :

image

可以找到ipipe_raise_irq()将interrupt推到pipeline

在ipipe上每个domain都有自己的优先度 高优先度的domain会先接收到interrupt 高优先度的domain的thread 可以preempt低优先度domain的thread

Xenomai 3

xenomai3有两种configuration:

  • Cobalt: 采用dual kernel架构,是xenomai 2的延伸

  • Mercury: 使用单kernel形式,在linux kernel上提供xenomai api,由于本身依赖linux,一般来说会以PREEMPT_RT提供real-time services

Xenomai 3 dual kernal configuration : Cobalt

image
多一个 priority 比 linux 还高叫 cobalt 的 core 去处理 real-time 的事情,提供不同的 real-time API 给不同的 applications 使用。并且利用Optimistic interrupt protection 机制减少 changing the interrupt mask,一般的机制在每次进入critical section时都要interrupt mask,而Optimistic interrupt protection可以不用。而real-time 在意的 “deadline”,实际上就是探讨 latency (latency 越大,系统越难在时限内完成完成高优先权任务,自然即时能力就越差),而 latency 很大的来源则是 interrupt handling。

具有实时内核cobalt、实时驱动模型RTDM、实时应用POSIX接口库libcobalt,然后再基于libcobalt实现的其他API skins,如Alchemy API、VxWorks® emulator、pSOS® emulator等(具体查看应用编程接口文档https://xenomai.org/documentation/xenomai-3/html/xeno3prm),即VxWorks、pSOS应用程序可稍微修改源码就可以在xenomai上编译运行。
需要说明的是Alchemy API是xenomai除posix外的官方编程接口,提供了更接近于传统RTOS编程方式的编程接口,对于不熟悉linux应用开发的MCU开发人员也能很快上手。在xenomai2上Alchemy API是xenomai的原生编程接口,性能最好,posix API是在Alchemy API上实现的skin。在xenomai3相反,Alchemy API、VxWorks、pSOS均基于posix接口实现,也正因为这样诞生了mercury方式。

Xenomai 3 single kernel configuration :Mercury

image

运用本机的 linux core 在 PREEMPT_RT之上达到 real-time 的事情,这里不是强制的,看 applications 对反应时间和 maximum jitter 的要求,有些甚至会作到某种程度 deadline 的忽略。

基于直接修改linux内核源代码的PREEMPT RT,应用空间在glibc之上,添加xenomai API库,如下图所示。可以在不支持cobalt内核时,可使用该方法运行xenomai应用;也就是说你还可以通过mercury方式在PREEMPT RT上编译运行VxWorks、pSOS等接口的应用程序。 当然,也可不需要PREEMPT RT,直接使用linux,只是实时性就……

xenomai3-cobalt 结构

image
image
在内核空间,在标准linux基础上添加一个实时内核Cobalt,得益于基于ADEOS(Adaptive Domain Environment for Operating System),使Cobalt在内核空间与linux内核并存,并把标准的Linux内核作为实时内核中的一个idle进程在实时内核上调度。

2000年,Karim发表了一篇名为《操作系统的自适应域环境》的论文(即Adeos,Adaptive Domain Environment of Operating System),该论文描述了一种简单而智能的方案,用于在同一系统上运行的多个内核之间共享公共硬件资源。他通过“pipeline”抽象来说明在x86硬件上共享中断的基本机制,根据整个系统给定的优先级,依次向每个内核传入中断。他倡导一种对硬件中断进行优先级排序的新方法,以便可以开发基于Linux内核的实时扩展,而无需使用当时已被某些专有RTOS供应商申请授予专利方法(这里的RTOS供应商和专利指的就是WindRiver和RTlinux使用的RTHAL技术)。 ADEOS (Adaptive Domain Environment for Operating System),提供了一个灵活的环境,可以在多个操作系统之间或单个OS的多个实例之间共享硬件资源,从而使多个优先级域可以同时存在于同一硬件上。早期在xenomai 2上使用。 2005年6月17日,Philippe Gerum发布用于Linux内核的I-pipe,I-pipe基于ADEOS,但是I-pipe更精简,并且只处理中断,xenomai3使用I-pipe。

ADEOS ,其核心思想是Domain,也就是范围的意思,linux内核有linux内核的范围,cobalt内核有cobalt内核的范围。

  • 两个内核管理各自范围内的应用、驱动、中断;
  • 两个domain之间有优先级之分,cobalt内核优先级高于linux内核;
  • I-pipe优先处理高优先级域的中断,来保证高优先级域的实时性。
  • 高优先级域可以通过I-pipe 向低优先级域发送各类事件等。

image

在用户空间,添加针对实时应用优化的库--libcobalt,libcobalt提供POSIX接口给应用空间实时任务使用,应用通过libcobalt让实时内核cobalt提供服务。
驱动方面,xenomai提供实时驱动框架模型RTDM(Real-Time Driver Model),专门用于Cobalt内核,基于RTDM进行实时设备驱动开发,为实时应用提供实时驱动。RTDM将驱动分为2类:

  • 字符设备(open/close, read, write, ioctl),如UART,UDD,SPI……
  • 协议设备(socket, bind, send, recv, etc),如UDP/TCP,CAN,IPC,……

中断方面,I-Pipe(interrupt Pipeline)分发Linux和Xenomai之间的中断,并以Domain优先级顺序传递中断。I-Pipe传递中断如下图所示,对于实时内核注册的中断,中断产生后能够直接得到处理,保证实时性。对于linux的中断,先将中断记录在i-log,等实时任务让出CPU后,linux得到运行,该中断才得到处理。

image

实时内核cobalt与非实时内核linux相结合,既能提供工业级RTOS的硬实时性能,又能利用linux操作系统非常出色的生态、网络和图形界面服务,在产品的开发周期和成本控制方面都有巨大优势 。

系统基本实现深度解析参考

xenomai内核解析--双核系统调用(一)
xenomai内核解析--任务同步互斥机制(一)--优先级倒置
xenomai内核解析--实时内存管理--xnheap
xenomai内核解析--信号signal(一)---Linux信号机制

xenomai proc文件信息介绍

1.1 实时外设中断信息

ipipe
ipipe为了xenomai与linux之间更好地结合,引入了虚拟中断。虚拟中断和常规softirq本质上不同,softirq只存在linux中,ipipe虚拟中断更近似于硬件中断,但不是硬件触发,由内核之间需要处理紧急任务时向另一个内核发送,ipipe处理虚拟中断与处理硬件中断流程一致,这些中断信息分为Llinux和xenomai,分别对应文件/proc/ipipe/Linux和/proc/ipipe/Xenomai。

dovetail

dovetail区别与ipipe,它是在已有linux中断管理代码上进行扩展,为实时核提供与ipipe相同的功能。所以/proc/interrupts就包含了ipipe层和xenomai实时中断信息。

1.2 查看实时核信息

xenomai内核相关信息均为位于/proc/xenomai/目录下:
image

xenomai调度cpu
ubuntu@work-host:/proc/xenomai$ cat /proc/xenomai/affinity
000000ff

/proc/xenomai/affinity 中是一组掩码,每一个bit表示系统中的一个CPU,该源码表示cobalt内核管理的CPU集,若实时应用程序中没有设置affinity,那么该实时会调度在该掩码下的任意一个CPU上。当我们需要将指定cpu给cobalt调度的时候,可以通过添加内核参数 xenomai.supported_cpus=0x06 来修改,这通常结合linux参数isolcpus来优化实时性能。

硬件timer与延迟信息

/proc/xenomai/clock/coreclk包含了系统的硬件timer和gravity信息。

root@xxxxx:/proc/xenomai# cat clock/coreclk
gravity: irq=1880 kernel=6880 user=6880
 devices: timer=timer2, clock=ipipe_tsc
watchdog: off
   setup: 1880
  ticks: 230615579053 (0035 b1c279ad)
  • gravity在前文autotune工具部分已经解释,它的值可通过autotune 命令来重新测量调整:
    root@xxxxx:/proc/xenomai# autotune
    == auto-tuning started, period=1000000 ns (may take a while)
    irq gravity... 2880 ns
    kernel gravity... 4320 ns
    user gravity... 7200 ns
    == auto-tuning completed after 38s
    root@xxxxx:/proc/xenomai# cat clock/coreclk
    gravity: irq=2880 kernel=4320 user=7200
     devices: timer=timer2, clock=ipipe_tsc
    watchdog: off
       setup: 1880
      ticks: 25641430824 (0005 f8592f28)
    
  • 如果latency测试后只修user gravity, echo xxxx > /proc/xenomai/latency即可:
    root@xxxxx:/proc/xenomai# echo 6680 > latency
    root@xxxxx:/proc/xenomai# cat clock/coreclk
    gravity: irq=2880 kernel=4320 user=6680
     devices: timer=timer2, clock=ipipe_tsc
    watchdog: off
       setup: 1880
      ticks: 28659743127 (0006 ac40f997)
    
  • 若三者都需要修改,可通过 echo irq=xxxx kernel=xxxx user=xxxx> /proc/xenomai/clock/coreclk 修改:
    root@xxxxx:/proc/xenomai# echo irq=0 kernel=0 user=0 > clock/coreclk
    root@xxxxx:/proc/xenomai# cat clock/coreclk
    gravity: irq=0 kernel=0 user=0
     devices: timer=timer2, clock=ipipe_tsc
    watchdog: off
       setup: 1880
      ticks: 32430637968 (0007 8d044390)
    
faults

文件/proc/xenomai/faults提供了cobalt内核实时上下文产生的fault统计信息,为什么需要关注CPU fault?回到实时性,如果我们的实时任务真正进行关键的行为,此时产生了异常,异常必须解决才能继续运行,这就导致了结果输出的不确定性,即影响实时性。这也是为什么我们在xenomai应用编程时不能通过glibc库进行动态内存分配的原因,因为linux内存的惰性内存分配机制,只有在应用访问分配的虚拟内存地址时才产生缺页异常(Page fault)进行物理内存分配,同时linux在内存水位过低时也会进行内存回收,需要考虑。这也是为什么VxWorks等RTOS很少使用MMU的原因。

该文件只统计cobalt内核管理的CPU且在实时调度上下产生的异常,因此,若你运行的实时任务抖动比较大,建议查看一下文件,是否是受到异常的影响。

X86下各类异常如下:

ubuntu@work-host:/proc/xenomai$ cat faults
TRAP         CPU0        CPU1
  0:            0           0    (Divide error)
  1:            0           0    (Debug)
  3:            0           0    (Int3)
  4:            0           0    (Overflow)
  5:            0           0    (Bounds)
  6:            0           0    (Invalid opcode)
  7:            0           0    (FPU not available)
  8:            0           0    (Double fault)
  9:            0           0    (FPU segment overrun)
 10:            0           0    (Invalid TSS)
 11:            0           0    (Segment not present)
 12:            0           0    (Stack segment)
 13:            0           0    (General protection)
 14:            0           0    (Page fault)
 15:            0           0    (Spurious interrupt)
 16:            0           0    (FPU error)
 17:            0           0    (Alignment check)
 18:            0           0    (Machine check)
 19:            0           0    (SIMD error)
heap
ubuntu@work-host:/proc/xenomai$ cat heap
    TOTAL      FREE  NAME
  4194304   4192256  system heap
   262144    262016  shared heap

该文件显示了xenomai heap的信息。前面说到,为避免实时性受到影响,实时应用运行过程中不能使用linux的惰性内存分配接口,在内核里也是一样,一是linux内核内存分配算法的不确定性,二是linux内核惰性分配原则。所以xenomai的解决机制是,xenomai内核初始化时预先分配一大片内存,并一一读写访问来建立虚拟内存与物理内存的映射,再通过自己的内存分配算法进行管理,该分配算法时间是确定的,xenomai自己管理的内存称为xnheap,xenomai运行过程中的动态内存分配均从xnheap中分配,为避免多个子系统同时访问一个xnheap,xenomai内核内有多个heap,这些heap的大小可通过内核参数 xenomai.sysheap_size=<kbytes> 配置,或内核编译时配置:

[*] Xenomai/cobalt  ---> 
     Sizes and static limits  --->
          (512) Number of registry slots                                   
          (4096) Size of system heap (Kb) 
          (512) Size of private heap (Kb) 
          (512) Size of shared heap (Kb)

关于 xnheap 内存分配管理算法,有单独的文章介绍。

registry

什么是registry?与内存资源类似,在操作系统管理应用程序或为应用程序提供服务的实时,需要管理很多对象。

举个例子,有两个xenoami实时任务, 它们使用semaphore做同步互斥,任务1创建一个名为/test-sem的semaphore,任务2打开这个semaphore并使用该semaphore,思考下面两个问题:

  • 问题1:任务1创建的这个semaphore是如何管理的?
  • 问题2:任务2又是如何通过name找到它的?

这里例子中的semaphore在操作系统内核中可以一种内核对象,registry提供了一个机制,用于保存xenomai全局内核对象。这些对象分为两种,一种有name,常用于两个及以上进程间,可以通过name来找到同一对象。另一种没有name,常用于同一进程空间。

registry的大小和heap一样预先分配,xenomai运行过程中直接获取,一是全局方便查找降低实现复杂度,二避免内存分配导致系统heap竞争。默认大小为512个条目。

ubuntu@work-host:/proc/xenomai$ cat registry/usage
10/512

若你运行在xenomai上的软件比较庞大和复杂,512个不够使用,可重新配置编译内核:

-> Xenomai/cobalt (XENOMAI [=y]) 
	-> Sizes and static limits 
	(512) Number of registry slots

关于registry内核对象的管理机制,会有单独的文章介绍。

1.3 调度与任务状态

下面看最重要的部分,xenomai内核和实时任务运行状态信息在/proc/xenomai/sched下:

image

其中/proc/xenomai/sched/rt/threads/proc/xenomai/sched/threads,前者仅包括优先级大于0的xenomai任务信息,后者包括所有xenomai调度的任务信息,比如我们在运行latency时,只有sampling线程是有优先级的,主线程和display线程优先级均为0。

线程状态
root@xxxxx:/proc/xenomai/sched# cat threads
CPU  PID    CLASS  TYPE      PRI   TIMEOUT       STAT       NAME
  0  0      idle   core       -1   -             R          [ROOT]
  0  265    rt     core       98   -             W          [rtnet-stack]
  0  266    rt     core        0   -             W          [rtnet-rtpc]
  0  281    rt     core        0   -             W          [rtnetproxy]
  0  380    rt     cobalt      0   -             X          latency
  0  382    rt     cobalt      0   -             W          display-380
  0  383    rt     cobalt     99   -             Wt         sampling-380
root@xxxxx:/proc/xenomai/sched# cat rt/threads
CPU  PID    PRI      PERIOD     NAME
  0  265     98      -          rtnet-stack
  0  383     99      -          sampling-380
  • CPU 表示该实时任务在哪个CPU的调度队列上。
  • CLASS 表示该任务的调度类
  • TYPE 中core表示该任务是内核态任务,cobalt用户态任务
  • PRI 表示任务优先级
  • STAT 表示任务所处状态
系统统计信息

下面看cobalt内核统计信息/proc/xenomai/sched/stat,这对我们查找实时问题时有帮助,以latency运行为例,输出如下:

root@xxxxx:/proc/xenomai/sched# cat stat
CPU  PID    MSW        CSW        XSC        PF    STAT       %CPU  NAME
  0  0      0          40051      0          0     00018000   99.1  [ROOT]
  0  265    0          2          0          0     00000042    0.0  [rtnet-stack]
  0  266    0          2          0          0     00020042    0.0  [rtnet-rtpc]
  0  282    0          2          0          0     00020042    0.0  [rtnetproxy]
  0  329    1          1          5          0     000600c0    0.0  latency
  0  331    40         80         44         0     00060042    0.0  display-329
  0  332    2          40003      40046      0     0004c042    0.6  sampling-329
  0  0      0          44791      0          0     00000000    0.3  [IRQ16: [timer]]
  0  0      0          0          0          0     00000000    0.0  [IRQ46: 4a100000.ethernet]
  0  0      0          0          0          0     00000000    0.0  [IRQ47: 4a100000.ethernet]

它统计了cobalt内核线程、用户态线程、中断的信息,这些信息包括:

  • CPU 运行的CPU和PID
  • MSW 域上下文切换。xenomai是双内核结构,xenomai应用可以无缝使用linux提供的服务,但这是有风险的,因为使用linux服务需要切换到linux非实时调度上下文,无法保证任务的实时性。MSW表示的就是实时域与非实时域之间的切换次数。

latency是主线程,它创建完高优先级线程sampling-329和负责结果打印的低优先级线程display-329后,等待SIGTERM等结束信号。间隔执行cat stat发现display-329的MSW会不断增大,是因为打印输出需要使用linux提供的服务进行终端打印;而高优先级sampling-329的MSW没有变化,因为该线程创建后,没有调用任何linux服务全程在实时内核上下文运行。为什么sampling-329 MSW为2? 因为xenomai实时线程的创建是通过linux来完成 的。

image

因此,如果你的实时任务会出现大的抖动,那么请通过该文件观察,是否因为不经意调用了linux的服务,切换到linux域引起的。

  • CSW xenomai内核上下文切换次数。
  • XSW 切换到linux域后,由linux内核调度的上下文切换次数。
  • PF 为Page fault产生次数。
  • STAT 运行状态掩码
  • %CPU CPU使用率,需要额外说明的是,[ROOT]表示的是cobalt内核调度下的linux,双核下linux已经退化为cobalt内核调度的一个任务。

Xenomai编程注意事项

Reference

https://www.cnblogs.com/wsg1100/category/1744176.html
SourceCode
http://wiki.csie.ncku.edu.tw/embedded/xenomai
https://blog.csdn.net/pupil_wjj/article/details/119456869
https://zhuanlan.zhihu.com/p/520467236
https://blog.csdn.net/tjcwt2011/article/details/81200111

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章