欢迎使用CSDN-markdown编辑器

Life with adeos

摘要

这篇文档是Adeos技术的概述和它为Xenomai提供的支持。介绍了解决事件管线、中断分发的实现和系统事件传播之后,我们将学习Xenomai与 Adeos之间的交互影响。我们将会解释首要和次要的域的概念并且我们对中断是怎样被传播的也感兴趣。我们最后将以总结一些启用/禁用中断源和在域间共享中断的技巧和窍门的方式结束。

什么是Adeos

Adeos是作为一个Linux内核补丁的资源虚拟层,它的通用设计早在2001年由 Karim Yaghmour在一个技术文件中提议的。这个提议的当前化身是它成为一个简单、高效的实时系统的推动者。它提供了一种方法来运行一个常规的GNU/Linux环境和一个RTOS在相同的硬件上并行执行。
为此,Adeos启用了多个实体——称作域——同时存在于同一个机器上。这些域不需要看见彼此,但是它们都能看见Adeos。一个域很可能是一个完整的操作系统,但是没有假设考虑域中的复杂性。然而,所有的域都可能争着处理外部事件(例如中断)或者内部的(例如陷阱、异常),根据它们已获得的全系统的优先级。
此外,其简单的虚拟化能力,另外一个关键的Adeos的优势在于它有能力导出一个通用的API到客户域,这个不依赖于CPU的体系结构。因此,许多如果不是大部分发生在客户域的移植工作会发生在Adeos层。

事件管线

必须牢记的是Adeos基本的结构是客户域征求事件控制链。域是一个内核软件组件可以告知Adeos层被什么通知:
1. 每一个传入的外部中断或自动产生的虚拟中断;
2. 每一个Linux应用程序产生的系统调用;
内核代码触发的其他的系统事件(例如,Linux任务切换,信号通知,Linux任务退出等等)
Adeos确保事件依据它们在系统中各自相应的优先级有条不紊的被分发到各个客户端领域,所以为这些事件提供及时和可预测的分发是可能的。这个优先级严格定义了事件被分发到域的顺序。所有活动的域依据它们各自的优先级排队,形成了Adeos使用的管线抽象来从最高优先级到最低优先级的域形成事件流。传入事件(包括中断)配推动到管线的头部(即,最高优先级域)处理到它的尾部(即,最低优先级域)。
下图给出了一个某个基于Adeos系统的通用试图,在这里多 个域通过管线抽象共享事件:

![Adeos_pipe_line.png](Adeos_pipe_line.png)

在上图中,Linux内核关于管线上的位置可以是任意的;也就是说,Linux内核还有一个特殊的作用因为它代表了根域,因为所有其他的域需要Linux去安装它们,通常意味着加载内核模块。

中断保护

为了以优先级的方式分发中断,同时仍允许域创建无中断部分,Adeos实现了Stodolsky,Chen和Bershad描述的所谓的乐观的中断保护模型。
任何给定的域占用管线的阶段都能被推迟,这意味着下一个到达的中断将不会被分发到域的处理者并将会被阻止以同样的举动流到最低优先级域。当一个阶段被推 迟,暂停的中断累积在域中断日志中,最终在这个阶段不再被推迟时被一个名为同步的内部操作处理。域使用这种特点来保护他们自己的临界区不被他们自己的中断 处理有害的抢占。然而,多亏Adeos带来的中断控制虚拟化,一个更高优先级的域仍能接收中断并且最终抢占任何低优先级域。实际上,这意味着,即使一个基 于Adeos的Linux内核经常推迟它自己的阶段来处理临界区操作,一个提前运行的实时系统在管线中仍能在任何时候够接收中断,不会产生任何延迟。
当一个域处理完了它所接收到的待处理的中断,它会调用一个特殊的Adeos服务让出CPU到管线的下一个域,所以后者可以依次处理唤醒它的待处理的事件并且这个周期会持续到管线的最低优先级域。

下图说明了运行在多CPU上的多个域怎样通过Adeos管线抽象共享传入的中断。当然,当一个阶段被推迟,必须正确的标记待解决中断:这是通过一个每域,每CPU中断日志实现的,见下面的解释:

![Ipipe_interrupt.png](Ipipe_interrupt.png)

系统事件传播

中断不是唯一一种可以流经管线抽象的事件;Linux内核自己触发的内部事件或者它的应用程序产生的所谓的系统事件。从根本上说,系统事件是陷阱、异常或某些Linux内核的动作的同步通知,并通过管线通知任何感兴趣的部分。
由于这些事件的本质是同步的,没有办法通过推迟操作推迟他们的唤醒,仅仅是因为你不能延迟他们的处理。这样设计决定的依据来源于这样一个事实:代码触发一 个系统事件可能仅仅是没有响应处理程序的干预不能继续处理:例如,缺页处理程序在内存寻址异常后应该立即执行,延迟它没有任何意义。换句话说,在给定的域 上的延迟/非延迟操作仅仅关心中断,不管是真实的还是虚拟的。

Adeos为Xenomai提供了什么?

相反,这个问题也可以是:Xenomai需要提供的实时服务的基本保证是什么?答案简单明了:在通知Linux内核之前,xenomai必须被允许, 第一个处理所有传入的中断,并且必须有能力立即处理他们, 而不管当前进程试图从Linux内核使用CPU中断掩码锁定它们。它也必须确保始终执行适当的线程的优先级管理不管他们当前所在的执行域。
这些保证给Xenomai可预测性的中断过延迟在最低的微秒层次范围内,不论Linux正在经历什么活动,一旦加在Linux任务的Xenomai快速协同调度技术(即,影子线程),提供了实时线程确定的调度延迟。下图说明了Adoes层在Xenomai体系结构中的位置:

Xeno_arch.png

你将会注意到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需要实现以下几点:

  1. 公共的优先级模型。只能调度,我们需要一种方法使实时核和Linux内核共享相同的优先级模型相对于线程共享控制。换句话说,一个Xenomai线程需要是 自己的优先级在任何时候严格生效,不管它当前的域,在所有的Xenomai线程中。Xenomai采用称为根线程可变优先级的技术,通过这个Linux内 核自动继承被实时核控制的Xenomai线程的优先级,这恰好发生在进入次要域的时候。实际上,这意味着当前运行在主要域的Xenomai线程没有必要抢占运行在次要域中的线程,除非它们的有效优先级确实更高。例如,上面的行为跟RTAI/LXRT相反,在RTAI/LXRT中线程迁移到Linux空间事实上会丢失它们实时有限级,通过继承RTAI调度器定义的最低优先级实现。也就是说,常规Linux任务队Xenomai一无所知,这仅仅发生在属于SCHED_FIFO的类中,当与来自主要域的Xenomai线程竞争CPU时总会被抢占,即使他们仍然会与运行在次要域中的Xenomai线程竞争优先级明智。
  2. 程序执行时间的可预测性。当一个Xenomai线程运行在Linux(即,次要)域,不管执行内核还是应用代码,它的时序不应该被非实时 的Linux中断活动搅动,通常来说被任何低优先级、发生在内核层的异步异步活动。一个简单的方法来阻止后者发生的几率是当Xenomai线程运行在 Linux域时使Linux内核保持中断饥饿,因此没有推迟处理会在这段时间内触发上半部中断处理。使Linux内核遭受中断饥饿的方法是当内部需要中间 Adeos域的时候阻塞他们,坐在被实时核和Linux内核占用的中断之间,在Xenomai术语中叫做中断屏蔽。无论什么时候Xenomai线程在Linux内核空间被调用这个屏蔽都会被占用,在其它的情况下不同。需要注意的是屏蔽支持能被启用/禁用在每个线程基础上,或者在Xenomai构建时系 统范围的基础上;默认情况下对于Xenomai是被禁用的并且不是内建的。
  3. 细粒度的Linux内核。为了从次要执行模式中获得最好,我们需要Linux内核表现出最短的可能非抢占部分,因此在Xenomai线程运行在次要域变成准备运行之后重调度的机会会尽快的被抓住。另外,这确保了 Xenomai线程能在一个短的和有时间界的一段时间内从主要域迁移到次要域,因为这个操作包含了到达一个内核重调度点。

系统调用

由于Xenomai实时API(即,skins)可以堆积在Xenomai的nucleus上,能将它们自己的一组服务导出给用户空间Xenomai线程, 必须有一种方法对适当的处理程序的分离对应的系统调用,而常规的系统调用都是在一起的。Xenomai拦截每一个Xenomai线程需要处理的 Xenomai或Linux域的系统调用陷阱/异常。这通使用适当的Adeos订阅事件处理程序实现。Xenomai使用这种能力来:
1. 将来自应用程序请求的实时服务分发给恰当的系统调用处理程序;
2. 确保每一个系统调用都会在其相应的域中执行,无论是Xenomai还是Linux;

中断传播

因为实时nucleus在管线的最前面,当有感兴趣的中断来临时在Xenomai域中的实时nucleus会第一个被通知,中断被处理之后,nucleus 会将该中断标记并传递到管线,如果必要最终会传递到Linux内核域。当被产生的中断唤醒时,实时nucleus会在外部中断处理程序返回后(以防中断堆 积)重新调度,并会转换成它控制的可运行线程的最高优先级。
当没有实时活动被阻塞时,Xenomai域会将CPU的控制权交给中断屏蔽域。也就是说当Xenomai域空闲时让出CPU,当Xenomai有事件处理时,抢占CPU。对于中断通过管线,Adeos有两种传播模式:隐式模式和显示模式。隐式模式由系统自动传播中断,显示模式需要手动传播中断。

技巧和窍门

启用/禁用中断源

除了能完全延迟一个域使不再有中断能经过它,直到它被明确的撤销延迟,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锁在管线层(别忘了实际上没有人从Xenomai域解决了期望的rthal_irq_enable()),这样的是不会发生的,因为Xenomai处理程序直到锁被移除才会运行的。因此,我们庆祝吧。
幸运的是,对于适当的共享中断有个方法,域间需要保持中断源禁用直到最终处理结束(例如,处理电平出发的中断是其中的一个问题):实际上,你不需要做任何事情,因为在把它传递给管线之前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);  
        /* This irq has been marked as pending for Linux */  
    }  
    void linux_eth_handler (int irq, void *dev_id, struct pt_regs *regs)  
    {  
        /* process the IRQ normally. */  
    }

结论

Adeos是相当简单的一段代码,如果使用得当包含了许多有趣的属性。Adeos模型的主干是时间管线,正因如此,它提供了所有我们在Xenomai中需要的重要特点:

  1. 可预测的中断延迟;
  2. 精确中断虚拟化控制(每个域和每个中断处理程序注册,每个域和每个CPU的中断屏蔽);
  3. 统一、优先级和面向域的事件传播模型;
  4. 一个通用和简单的API来简化客户代码的移植。

Xenomai使用这些特点来寻求Linux内核带来的实时服务的最大可能的集成。Xenomai的主要模式在最低的微秒即的延迟提供真的实时性能。另外,Xenomai在Linux未来演化中赌注,来改善内核的总体粒度,例如PREEMPT_RT,因此次要模式仍在在确定意义上的实时,即使是最坏情况 下的延迟也是可测的。这就是为什么Xenomai从第一天开始就努力工作来达到与Linux内核高度集成的层次。考虑共生,寻求共利。

链接

Karim Yaghmour’s Adeos 提议

乐观中断保护

GNA上的Adeos工作目录

下载最新的Adeos补丁

Adeos API 参考手册

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值