1、概述
RTA-OS是一种静态可配置的抢占式实时操作系统(RTOS),用于高性能、资源受限的应用程序。属于ETAS厂商的AUTOSAR操作系统。
OS本质是为了解决以下几个基本问题
改变各任务的执行频率;
改变各任务的执行时间;
设定各任务的优先级,保证高优先级任务能够及时执行;
任务切换时的现场保护与恢复;
共享资源的安全访问机制等;
AUTOSAR OS总共包含以下6大基本对象:Counter,Alarm,Schedule Table,Task,ISRs,Resource。这6个基本对象必须归属于一个OS Application,可以简单理解为OS Application是上述6大基本对象的容器,而归属于同一OS Application的基本对象则可以互相访问,来自其他OS Application的基本对象则需要通过配置来限制性访问。
文章按照下面思维导图进行介绍
RTA-OS内核有以下特点
1.1、high performance(高性能)
内核非常小,速度非常快。内核的内存占用及其运行时性能是同类OS中领先的,使得RTA-OS特别适用于大量制造的系统,在这些系统中,必须满足对硬件成本的非常严格的限制,并且任何最终产品都必须正确运行。
RTA-OS提供了许多独特的优化,有助于降低系统的单位成本。内核对所有类型的任务都使用了单堆栈体系结构。与传统的每个任务的堆栈模型相比,这节省了大量的RAM。此外,仔细的应用程序设计可以利用单堆栈体系结构来提供显著的堆栈RAM节省。
这意味着所有任务和ISR都运行在单个堆栈上。单个堆栈就是应用程序的C堆栈。
在单堆栈模型中,堆栈大小与系统中优先级的数量成正比,而不是任务/ ISR的数量。这意味着共享优先级的任务,无论是直接共享,还是通过共享内部资源,或者通过配置为非抢占,都不能同时在堆栈上。在硬件上共享优先级的ISR也是如此。这意味着您可以通过简单地更改配置来交换系统响应性(即任务或ISR完成所需的时间),以换取堆栈空间。
单堆栈分析图示
离线工具分析您的操作系统配置,并使用这些信息构建尽可能小且最快的内核。您不打算使用的代码被排除在内核之外,以避免浪费执行时间和内存空间。
1.2、real-time(实时)
传统的RTOS设计通常具有不可预测的开销,通常取决于任务的数量和系统在每个时间点的状态。这使得保证实时可预测性变得困难——无论内核有多“快”。在RTA-OS中,内核是快速的,而且所有的运行时开销——比如切换任务、处理中断和唤醒任务——都有较低的最坏情况边界,执行时间很少或没有变化。在许多情况下,上下文切换发生在恒定的执行时间内,这意味着RTA-OS可以用于开发硬实时系统,其中必须在特定的时间期限内做出响应。满足严格的截止日期包括计算每个任务和中断服务例程(ISR)的最坏情况响应时间,并确保每次都按时运行。RTA-OS是一个真正的RTOS,因为它满足了固定优先级可调度性分析的假设。
1.3、portable(可移植性)
RTA-OS可用于各种各样的微控制器/编译器组合(or port)。所有端口共享相同的公共RTA-OS代码,这约占总内核功能的97%。该内核是用与MISRA-C 2012兼容的ANSI C编写的。通过离线工具,可以生成RTA-OS的MISRA报告。
RTA-OS尽可能地不会对硬件施加控制。一般来说,不需要移交对硬件的控制,比如缓存、监视器计时器和I/O端口。因此,您的代码可以自由地使用硬件,从而允许将遗留软件集成到系统中
2、基础概念信息
2.1、任务的四种类型
任务可以是
·基本的或扩展的
·可以共享优先级
·可以排队激活
RTA-OS支持所有一致性类,并将从您的操作系统配置中计算一致性类。一致性类,RTA-OS里面没有配置的。
为了使RTA-OS尽可能高效,应该只使用基本任务,而不是共享优先级。
所以一致性类就出现了如下四个
2.1.1、BCC1任务
具有唯一优先级和非排队激活的基本任务(Basic taskswith unique priority and non-queued activation)。这些都是最简单的任务形式,非常适合用于硬实时系统。一旦任务被激活,它必须运行并终止,然后才能再次被激活。这种类型的任务不能在执行中途挂起自己以等待事件。在RTA-OS中,这些任务被称为BCC1任务,因为它们对应于OSEK OS的BCC1一致性类(conformance class)
所有任务都具有不同的优先级,且每个任务只能被激活一次。
2.1.2、BCC2任务
具有共享优先级和排队激活的基本任务(Basic taskswith shared priority and queued activation)。这些任务可以与系统中的其他任务共享优先级,并且在再次被激活之前不需要终止。操作系统排队等待任务激活的队列,并在当前激活终止时运行下一次激活。与BCC1任务一样,这种类型的任务不能在执行过程中挂起自己以等待事件。在RTA-OS中,这些任务被称为BCC2任务,因为它们对应于OSEK OS的BCC2一致性类。
每个优先级允许有多个任务,且每个任务允许被激活多次
2.1.3、ECC1任务
具有唯一优先级的扩展任务(Extended taskswith unique priorit)。允许扩展任务在执行过程中等待事件(即,该任务可以自挂起)。但是,激活不能被排队,并且任务必须具有唯一的优先级。在RTA-OS中,这些任务被称为ECC1任务,因为它们对应于OSEK OS的ECC1一致性类。
2.1.4、ECC2任务
具有共享优先级的扩展任务(Extended tasks with shared priority.)。这些任务类似于ECC1任务,但可以与系统中的其他任务共享优先级。在这方面,它们类似于BCC2的任务。然而,与BCC2任务不同的是,扩展任务不能有排队激活。在RTA-OS中,这些任务被称为ECC2任务。
系统可以包含上述任务类型的任何组合。
标准(Stand):是“精简和平均版本”,并提供最小的错误处理。
扩展(Extended):是“调试版本”提供广泛的错误检查功能。
2.2、基本对象
基本对象 | 功能含义 |
Count | 用于作为Alarm或者Schedule Table的触发基准 |
Alarm | 用于定时触发某个Task或者Event |
Schedule Table | 用于定时同步触发多个Task或者Event |
Task | OS调度的基本功能单元 |
ISR | 中断资源 |
Resource管理 | 简单的二进制信号量,允许在任务和中断之间共享的临界区上提供互斥。资源由操作系统使用优先级天花板协议进行管理,该协议保证不出现死锁,并最大限度地减少运行时的优先级反转。 个人理解:保护数据一致性,通过临界保护区等机制,确保系统的稳定性与可靠性 |
每一个Core可包含1N个OS Application,而每一个OS Application可包含0N个基本对象。每个基本对象必须从属于某个OS Application,否则会出现错误。同时OS Application可分为Trusted与Not Trusted这两种类型。
Trusted 与Not Trusted 的OS Application在运行过程中可以配置相应的监控与保护机制。从属于Not Trusted OS Application的OS基本对象对存储器和API的访问将受到限制,通常会将一些基础软件的模式管理主函数映射到Not Trusted OS Application中的任务,如EcuM_Mainfunction,BswM_Mainfunction, Can_Mainfunction_Mode()等周期性状态查询函数,当然前提这些软件模块的安全级别为QM。
2.2.1、Task
AUTOSAR OS中存在两种任务:基本任务(Basic Task)和扩展任务(Extended Task)。基本任务则存在以下三种状态:
运行状态(Running):处于运行状态的任务可能被高优先级任务或者中断抢占从而进入就绪状态,且同一Core中任何时刻只会存在一个任务处于运行状态,任务运行结束后则将自己挂起进入阻塞状态;
就绪状态(Ready): 处于就绪状态的任务由调度器决定是否启动进入运行状态,且该状态时任务切换至运行状态的前提;
挂起状态(Suspend): 处于阻塞状态的任务是被动的,可以由API函数或Alarm激活进入就绪状态;
扩展任务与之相比,则多了一个等待状态(Waiting),解释如下:
等待状态(Waiting): 当任务的运行需要等待某一或某些事件被置位时,任务进入就绪状态。
激活并不会导致任务运行——它只是让任务准备好运行。
RTA-OS需要检查激活的任务是否比当前运行的任务具有更高的优先级,如果是,则导致上下文切换,以便新任务可以抢占当前运行的任务。
激活可以通过多种方式发生,例如在代码中调用ActivateTask() API,或者作为某些触发器的结果,例如警报(Alarm)到期或调度表到期点( schedule table expiry point)。
如果有更高优先级的任务准备运行,则当前正在执行的任务将被抢占,并从运行状态移至就绪状态。这意味着在同一时间内只能有一个任务处于运行状态。
任务终止后返回挂起状态。任务可以在以后再次准备好,整个过程可以重复。
基本任务和扩展任务在准备、运行和挂起状态方面表现相同。但是,扩展任务也可以进入等待状态。当扩展任务通过等待事件自动挂起自己时,它将从运行状态转移到等待状态。
AU TOSAR OS(因此,RTA-OS)中的任务是静态定义的。这种技术被广泛使用,因为它节省了RAM和执行时间。任务不能动态创建或销毁。关于任务的大部分信息可以离线计算,允许它存储在ROM中。
如果低优先级任务在高优先级任务之前启动,那么在低优先级任务运行期间,高优先级任务将被阻止执行。这叫做阻塞。
即使一个任务不是可抢占的,它仍然可以被ISR中断。
当一个任务从ISR激活时,它永远不会立即进入运行状态,因此不需要检查上下文切换。
基本任务的代码示例如下:
#include “OS.h”
TASK(BasicTask)
{
...
/*User Code*/
...
TerminateTask();
}
扩展任务的代码示例如下:
#include “OS.h”
TASK(ExtendedTask)
{
for(; ;)
{
WaitEvent(Event1);
/*Perform some actions in specific condition*/
ClearEvent(Event1);
}
}
基本任务与扩展任务的状态机切换如下图
基本任务没有等待状态,所以只能在任务启动与终结时进行同步,基本任务的优点就是占用较小的任务与执行时间。
扩展任务的优点是包含多个同步点,没有同步请求的麻烦,当进一步的条件无法满足时,任务则会切换至等待状态,其缺点也很明显,会占用较多的内存和执行时间。
Task的调度机制
AUTOSAR OS是基于优先级进行任务调度,所以每个任务必定有一个优先级,而每个任务都是根据其自身特点来定义一个优先级且需要配置其可抢占属性。
可抢占属性可分为不可抢占与全抢占,这里所说的抢占指的是内核抢占。AUTOSAR OS可根据各个任务的可抢占属性配置,来提供不同的调度策略,调度策略可分为以下三种:
- 完全抢占式:OS所有任务均是可抢占类型;
- 非抢占式:OS中所有任务均是不可抢占的;
- 混合抢占式:OS部分任务是可抢占类型,部分任务是不可抢占类型;
1.对于完全抢占式任务调度策略而言,当前运行的任务可在任何时刻被高优先级任务打断而被迫释放处理器控制权,具备最高优先级的任务从就绪状态转入运行状态,而当前任务被抢占从而进入就绪状态,同时保留现场环境,待下次运行时恢复。
如下图所示为完全抢占式任务调度策略,TaskA为扩展任务,TaskB与TaskC为基本任务,优先级TaskA > TaskB > TaskC。
Case1:
当前TaskC处于运行状态,当激活TaskB进入到就绪状态时,由于TaskB优先级高于TaskC,所以TaskC被迫释放处理器控制 权,调度器 开始调度TaskB从就绪状态变为运行状态,直到TaskB运行完成之后,在调度TaskC继续运行。
Case2:
当前TaskC处于运行状态,激活TaskA与TaskB分别进入就绪状态,由于TaskA优先级高于TaskB,所以TaskA抢占内核运行, 但是由于Resource1仍被TaskC暂用,而TaskA无法访问到共享资源Resource1,则被迫进入到等待状态,TaskB开始运行。
TaskB运行结束后挂起之后则重新运行TaskC,TaskC运行结束后释放Resource1,进入TaskA得以由等待状态转入运行状态。此时你会 发现高优先级的任务TaskA由于共享资源被占用的原因导致不能先于TaskB运行的现象,该现象也被称为优先级反转现象。
为了解决该问题,在此需要提到AUTOSAR OS的优先级天花板模式:即将访问共享资源的任务优先级在占用资源的过程中提升至共享资 源任务的最高优先级之上,从而避免优先级反转现象的发生。
即若TaskC运行过程中占用共享资源Resource1,此时即使存在需占用共享资源的高优先级任务TaskA被激活,也必须保证TaskC运行结 束之后才能执行TaskA,也就意味着在重要代码执行之前,应采用资源保护机制,以免被高优先级的任务打断。
2. 若采用非抢占式调度策略,那么当前运行状态的任务在任何时刻都不会其他高优先级任务所抢占,任务的切换只会发生在任务完时。 非抢占式调度策略的问题在于任务执行时间不确定,系统调度实时性较差。如下图所示为非抢占式调度策略,可见即使高优先级任务 TaskB被激活切换至就绪状态,也必须等到TaskC执行结束之后才能够被调度。
3.若采用混合抢占式,则OS的调度策略就取决于当前任务的可抢占属性,如果为非抢占,则执行非抢占式调度策略,如果为抢占式则执 行完全抢占式调度策略。
一般情况下我们可以把初始化的任务,以及一些模式切换时候,必须进行的一些操作,这类任务设计成非抢占的任务。
任务优先级
AUTOSAR OS允许任务共享优先级。当任务具有相同优先级时,具有共享优先级的每个任务将相互排斥运行。这意味着如果一个任务正在运行,那么它的执行将与所有其他具有相同优先级的任务序列化。
当任务共享优先级时,它们将按照先进先出(FIFO)的顺序从就绪状态释放。
Note:当共享优先级和排队任务激活一起使用时,RTA-OS在优先级级别上维护一个内部队列。如果想要一个快速高效的操作系统,你应该避免这种类型的配置。
如果需要序列化一组任务的执行,那么最好使用唯一的优先级和AUTOSAR OS的内部资源来实现,而不是共享任务优先级。使用内部资源保证了序列化,就像共享优先级一样,任务优先级的唯一性意味着当多个任务同时就绪时,操作系统有一个静态定义的分派顺序。
Note:在任务之间共享优先级是一种糟糕的实时编程实践,因为它会阻止您在系统上执行可调度性分析。这是因为,在一般情况下,共享优先级使得任务的释放点(即测量响应时间的点)在计算上无法计算。如果不可能计算出何时发布,那么就不可能决定任务是否会在截止日期前完成!
任务队列激活
在大多数情况下,只能在任务处于挂起状态时激活它。AUTOSAR OS在任务处于就绪、运行或等待状态时将其激活视为错误情况。
然而,在某些情况下,我们可能需要实现这样一个系统:相同的任务必须多次激活,但连续激活之间的最短时间可能小于运行任务所需的时间。例如,您可能正在一个任务中解包CAN总线帧,并且需要处理网络上帧的瞬时爆发( transient bursting)。
这意味着我们需要在运行时排队等待任务激活。AUTOSAR OS操作系统允许排队激活基本任务,以帮助构建这类应用程序。与自动共享操作系统中的其他东西一样,任务队列的大小也是静态配置的。我们必须指定该任务可以挂起的最大激活数。
如果在尝试激活任务时队列已满,则这将作为错误处理,激活将被忽略。
自启动任务
任务可以自动启动,这意味着当操作系统启动时,它们将在StartOS()期间自动激活。
对于启动、运行然后终止的基本任务,自动启动任务将使它只运行一次,然后才返回挂起状态(在那里它可以再次被激活)。
2.2.2、Count
Counter概念的引入是为了实现对硬件计数器以及软件计数器的管理,为Alarm与Schedule table提供支持。即多个Alarm可以共用一个Counter,一个Schedule Table只能由一个Counter来驱动。 Counter按照AUTOSAR定义可分为以下两种:
Hardware Counter: 该Counter的增加由硬件外设驱动,如Gpt或者timer等;
Software Counter: 该Counter的增加通过调用API函数IncrementCounter来实现,且每次只能增加1;
基本原则: 优先使用Hardware Counter,因为可以根据Task的激活状况来减少无意义的时钟中断
OS Counter,Schedule Table,Alarm三者之间的关系如下图:
个人理解上Count就是计数器,例如Gpt定时器。
2.2.3、Alarm
在计数器的基础上,AUTOSAR OS为应用软件提供了闹钟机制,多个闹钟可以连接一个Counter,当到达Alarm所对应的计数器设定值时,则可以激活一个任务,设定一个event,调用callback或者增加计数器等功能,但只能是一对一。不能像Schedule Table那样,能够在Expiry point同时设定多个Task或者多个Event,这也是为什么引入Schedule Table的原因。
一个软件Counter +多个Alarm队列就可以实现静态定义的任务激活机制。 但随着Schedule Table的引入**,因此一般建议能用Schedule Table就不要用Alarm。**
个人理解:Alarm相当于一个阈值,例如1ms,到了这个1ms的ticks就激活一个Event,但是呢,只能激活一次,不如调度表灵活。
Alarm Callback只在 SC1 中提供。
2.2.4、Schedule Table
如上Alarm所述,当计数器的计数值依次达到各个Alarm设定的计数值时,各个Alarm被触发,但很难保证各个Alarm有特定的时间间隔,且每个Alarm只能激活一个Task或者Event,所以需要多个Alarm来协作实现在同一时刻触发多个Task或者Event,因此Schedule Table应运而生!
Schedule Table会定义一系列终结点(Expiry Point),且每个调度表都有一个以Tick为单位的持续时间(Duration)。每个终结点则是以Tick为单位的距离起始点的偏移量(Offset),在每个终结点可以实现多个Task或者Event的设置。
与报警器类似,一个调度表只能由一个Counter驱动,同时调度表存在以下两种调度方式:
单次执行(Single-Shot): 调度表启动之后 只运行一次,到达调度表终点则终止,即每个终结点只运行一次;
循环执行(Repeating): 调度表启动后可反复执行,到达调度表终点后重头开始执行,则每个终结点会被周期性的执行,一般情况下激活任务采用此模式。
如下图所示,较为清晰了描述了调度表中Expiry Point与Task,Event激活之间的时序关系。
注意点:
- 每一个终结点必须配置至少一个Task或者Event;
- 每一个调度表至少存在一个终结点(Expiry Point);
- 在每一个Expiry Point优先激活Task,随后设置Event;
分配任务的优先级,常见方案有:
·Deadline Monotonic Assignment: 更高的优先级被分配给最后期限较短的任务。
·Rate Monotonic Assignment: 更高的优先级被分配给需要更频繁地运行的任务。
首先说一下EP(Expiry Point),它相当于一个时间点执行对象,当时间到了,Schedule Table就会去执行这个EP,EP可触发多个Task或者触发多个Event,而一个Schedule Table 可配置多个 EP(Expiry Point)。
2.2.5、ISR
在AUTOSAR中定义了两类中断服务程序(Interrupt Service Routine)。分别为一类中断(Category I)与二类中断(Category),两者之间的区别定义如下:
Category I:此类中断服务程序不能够使用OS提供的系统服务,当中断执行完成之后则会重新跳转至产生中断的地方继续执行,不会影响到任务的执行,因此占用系统资源较少。
Category II:该类中断则可以调用OS系统服务,如激活任务或者设置事件等。
在AUTOSAR OS中,中断的优先级始终高于任务的优先级,即最低优先级的中断都可以打断最高优先级的任务,即使该任务不可抢占也不例外。 因此,中断服务子程序的执行时间不宜过长,否则会影响到整个系统的实时性。
一类中断可以使用部分OS的函数,例如使能与禁止
二类中断,当硬件终端触发,并且ICU模块处理完成之后,到达了OS,这个时候根据静态配置进行操作,对上下文进行切换与保存现场,如下图。
2.2.6、Resource Management
Resouce作为OS调度过程中一个十分重要的对象,Resource管理就显得尤为重要。资源管理就是为了协调具有不同优先级的多个任务或者中断对共享内存(如内存或者硬件等)的并发访问。
不过幸运的是AUTOSAR OS采用上述的优先级天花板模式来避免任务优先级反转以及死锁问题的发生,即资源的上限优先级必须高于所有该资源的任务以及中断的优先级,但是应低于不访问该资源的任务的最低优先级。
其中为了保护共享资源而提出的锁机制-自旋锁(Spin Lock)。该自旋锁一般用于多核操作系统解决资源互斥的问题。当内核控制必须访问共享数据结构或进入临界区是,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋所的保持者已经释放了该锁,从而达到某共享资源的互斥作用。
常用函数接口
为了便于大家日常软件调试,我将常见的OS相关API函数接口及其相应功能表述如下图所示:
单堆栈模型:单堆栈模型还显著简化了链接时的堆栈空间分配,因为您只需要为整个系统堆栈分配一个内存部分,就像您完全不使用操作系统一样。
RTA-OS堆栈不需要配置的原因是单堆栈。
Resource的函数一般情形如下
#include TASK(Task1) {...GetResource(Resource1);/* Critical section. */ReleaseResource(Resource1);...TerminateTask();}
操作系统提供基于资源的互斥机制。资源只是一个二进制信号量。当一个任务或二类ISR获得资源时,其他任务或ISR无法获得该资源。这可以防止任何其他任务或ISR同时进入相同的临界区。当临界区结束时,任务或ISR释放资源,另一个任务/ISR可以进入临界区。
2.3、强制性堆栈信息
计算出的最坏情况分派点定义了字节数,相对于调用StartOS()时堆栈指针的地址,扩展任务需要从该地址开始。这些偏移量作为ROM数据存储在扩展任务控制块中,并在运行时添加到堆栈的基址中。
这意味着需要告诉RTA-OS有关堆栈使用的各种参数。捕获的值是特定于端口的,您应该阅读您的端口的目标/编译器端口指南以获得额外的指导。
RTA-OS提供了运行时特性,用于测量任务和ISR的最坏情况下的堆栈值。
通常,所有端口都将允许您指定以下值。所有数字均以字节为单位:
用于C启动的堆栈(Stack used for C-startup, SpPreStartOS)在调用StartOS()时已经在使用的堆栈量。
该值被简单地添加到操作系统在运行时支持所有任务和中断所需的总堆栈大小中。
通常,您使用它来获得链接器必须分配的堆栈数量。
在计算最坏情况分派点时不需要这个值,可以安全地将其设置为零,除非您希望在堆栈使用情况报告中计算最坏情况的总体堆栈使用情况。
正常情况下,当操作系统配置发生变化时,该值不会改变。
2.4、空闲时使用的堆栈
操作系统处于空闲状态(通常在Os_Cbk_Idle()内部)时使用的最大堆栈量。这只是调用StartOS()时使用的堆栈与没有任务或中断运行时使用的堆栈之间的区别。如果没有使用Os_Cbk_Idle(),该值可以为零。它必须包含在空闲状态下被调用的任何函数所使用的堆栈。
正常情况下,当操作系统配置发生变化时,该值不会改变。
Os_Cbk_Idle的行为与任务相同,除了:
•无法激活
•不能终止
•它不能等待事件
•它不能被束缚
•不能使用内部资源
Os_Cbk_Idle的优先级比系统中的任何任务都低,因此它只在没有准备运行的任务(或ISR)时运行。
因此,空闲机制为您提供了一个几乎完全不受系统开销影响的“额外任务”。
Os_Cbk_Idle在退出时返回一个布尔值,告诉RTA-OS是否再次调用Os_Cbk_Idle。当返回TRUE时,RTA-OS立即再次调用Os_Cbk_Idle。当返回FALSE时,RTA-OS停止调用Os_Cbk_Idle,并进入繁忙等待循环的默认行为。
2.5、ISR激活的堆栈开销
与从task中激活任务相比,从ISR中激活任务所需的额外堆栈量。如果一个任务在类别2 ISR中被激活,并且该任务比任何当前运行的任务具有更高的优先级,那么对于一些与激活优先级较低的任务相比,目标操作系统可能需要使用更多的堆栈。这个值说明了这一点。在大多数目标上,这个值为零。
该值用于最坏情况下的堆栈大小计算。
当操作系统配置有较大变化时,该值可能会发生变化。
2.6、ECC任务的堆栈开销
启动ECC任务所需的额外堆栈量。ECC任务在启动时需要比BCC任务在堆栈上保存更多的状态。此值包含差值。
这个值可以通过测量堆栈值得到:
l在基本任务(向上)激活之前,立即在被激活任务的入口函数中。
l在扩展任务(向上)激活之前,立即在激活任务的入口函数中。
然后用第一个值减去第二个值。
当操作系统配置有较大变化时,该值可能会发生变化。还需要注意的是,如果你正在使用堆栈重定位(将不受信任的代码堆栈对齐以适应MPU),那么你将需要减少调整量的值。
2.7、ISR的堆栈开销
用于服务第二类ISR的堆栈数量。当二类ISR中断一个任务时,它通常会在堆栈上放置一些数据。如果ISR测量堆栈以确定被抢占的任务是否超过了它的堆栈预算,那么它将高估堆栈使用情况,除非从测量的大小中减去这个值。
该值也用于计算系统最坏情况下的堆栈使用情况。该值可以通过在中断之前测量堆栈值,并在测试期间立即在类别2 ISR的输入函数中获得。注意要准确地设置这个值。如果它的值太高,那么当发生减法时,就会发生32位下溢,并导致操作系统认为已经检测到预算超支。
当操作系统配置有较大变化时,该值可能会发生变化。
Note: 除了ISR (SpPreemption)的堆栈开销外,所有的强制堆栈值都会在检查期间添加到堆栈值中。这意味着指定比实际发生的更大的值是安全的。但是,ISR的堆栈开销(SpPreemption)将从进入ISR时的堆栈指针的当前值中减去,以检查被抢占的任务或ISR是否已经超过了它的堆栈使用量。因此,在这里指定一个大的值可能会导致报告一个没有发生的错误(即RTA-OS堆栈管理将报告一个“false positive”。
2.8、并发
必须同时执行许多不同活动的系统称为并发。这些活动可能包含一些软件部分,因此提供这些活动的程序必须同时执行。这些程序必须在任何必要的时候进行合作,例如,当它们需要共享数据时。
实时系统中的每个并发活动都由一个任务来表示。大多数应用程序代码都存在于任务中。如果有许多必须同时执行的任务,那么将需要提供一种允许并发性的方法。其中一种方法是为每个任务都有一个单独的处理器。可以使用并行计算机,但这个解决方案对许多应用程序来说太昂贵了。
实现并发行为的一个更经济有效的方法是在单个处理器上一次运行一个任务。然后可以在任务之间切换,以便它们看起来同时执行。
2.9、Pre and Post Task Hooks
假设需要在每个任务开始之前和/或在每个任务结束之后执行一些代码,例如分析执行的跟踪。可以使用AUTOSAR OS提供的PreTask和PostTask钩子来实现这一点。
当任务进入运行状态时,RTA-OS会调用PreTask钩子。
这意味着当一个任务在抢占后恢复时,PreTask钩子也将被调用。
当任务移出运行状态时,RTA-OS会调用PostTask钩子。
PostTask钩子将在任务终止时调用,并且每次任务被抢占时调用。
在任务进入和退出时以及每次抢占/恢复时调用PreTask和PostTask钩子。这意味着可以使用这些钩子记录应用程序的执行跟踪。由于应用程序中的所有任务都必须使用相同的PreTask和PostTask钩子,因此有必要使用GetTaskID() API调用来确定在进入钩子例程时哪个任务已经或将要运行。
2.10、可伸缩性类
SC1 | 标准OS |
SC2 | +时间保护 |
SC3 | +内存保护(注意没有时间保护) |
SC4 | +时间保护 +内存保护 |
2.11、核间通信
“IOC”负责操作系统应用程序之间的通信,特别是跨核心或内存保护边界的通信。它的内部功能与操作系统紧密相连。IOC提供通信服务,需要在同一ECU上跨操作系统应用程序边界通信的客户端可以访问这些服务。RTE使用IOC服务跨这些边界进行通信。所有通信必须通过发送方(或客户端)和接收方(或服务器)端的RTE进行路由。
3、安全措施
3.1、堆栈监控
堆栈回调的配置在OS里面如下图
这里面就是可以激活OS 对栈使用的监控。os将会自动monitor
函数如下
实现代码示例如下
实现图示
从图中可以看出来。不同地方调用 GetStackUsage结果可能是不同的。
所以一般来说,我们可以选择高优先级的任务进行执行该API. 进而获取栈相关信息。
堆栈出问题的钩子函数如下:溢出字节与溢出原因会被传递进来。
3.2、时间保护
时间保护是为了保证程序的正确执行,配置上倒是很简单如下图
3.3、内存保护
内存保护的机制通过os-application 的 trust 和 non-trust 进行隔离开。对读写的限制,对运行的隔离。
当其中一个os-application 发生故障,可以独立的关闭其中一个os-application ,其他的os-application 不受影响。
对外设的访问,对内存特殊区域的访问的约束。可以通过trust non-trust 来约束。
这样可以一定程度上减少 让os崩溃的可能发生
注意一下内存保护是所选取MCU的功能,不属于软件功能。
4、使用注意事项
系统服务函数“ChainTask()”会终止当前正在运行的任务,同时激活一个新任务,然后会触发一次系统调度。一般不建议使用这个,目前看来用扩展任务,然后用ActivateTask()激活一次就行了。