前言
- 为什么汽车电子ECU需要使用OS呢,它的必要性在哪里?
今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:
正文
为啥要用OS?
我们知道传统所说的“裸机编程”就是不带操作系统的编程,在系统需求相对比较简单的情况下使用裸机编程可以满足要求。但是随着系统需求越来越复杂,此时就需要用到模块化设计方法以及多任务编程思想,否则后期软件升级维护成本将会急剧增加。虽然我们可以采用传统编程方式(如计数器与状态机)来实现简单多个任务的调度,但是当涉及到多个任务之间的状态切换,优先级,现场保护,执行时间控制等方面就显得极为吃力,开发效率低下且极容易出错。此时迫切需要一种机制来替我们完成各个任务之间的调度功能,使得开发人员能够更关注于应用软件的开发,提高软件开发效率。为此OS(Operation System)便应运而生!实际上,OS主要是为我们解决了以下几个基本问题:
- 设定各任务的优先级,保证高优先级任务能够及时执行;
其中调度功能则是OS的核心组件,主要功能就是负责任务的切换。在一个单核系统中,多任务只能并发执行,通过时间片轮转法来切换任务。通过时钟中断或者软中断的方式来触发一次任务的切换,从而打断当前执行任务,调度器抢夺CPU控制器,来进行任务调度并切换至新任务开始执行,如下图1所示:
对于传统汽车电子开发领域,早期使用的OS则是OSEK OS, 其中OSEK是德文的缩写,译为汽车电子开放系统及接口。OSEK OS是一个为满足汽车电子可靠性、实时性、成本敏感性等需求而打造的实时单核操作系统(RTAOS)。该实时单核操作系统具备以下基本特性以及基本系统服务,如下图2所示:
AUTOSAR OS与OSEK OS联系
首先,AUTOSAR OS是基于OSEK OS继承发展而来,所以上述的OSEK OS的基本特点在AUTOSAR OS都能够得到满足,所以AUTOSAR OS是向后兼容的,也就意味着在OSEK OS上能够运行的应用程序同样也可以在AUTOSAR OS上运行。除此之外,AUTOSAR OS也存在自身的一些独特的基本特点,下面将从该OS的基本属性与系统基本服务两个方面展开:AUTOSAR OS在OSEK OS的基础上,除了上述的基本特点之外,仍需要确保具备以下几点十分重要的属性:
AUTOSAR OS继承OSEK OS,在OSEK OS的基础上又特别明确了AUTOSAR OS至少需要提供的系统服务如下:
基于优先级的调度;
及时的中断处理的能力;
中断优先级必定高于task;
通过StartOS()与StartOSHook()来创建启动接口;
通过ShutdownOS()与ShutdownOSHook()来创建关机接口;
能够在OSEK OS中跑的APP自然也能够在AUTOSAR OS运行,但同时Autosar os也同时限制了OSEK OS的一些基本使用;
AUTOSAR OS基本对象
AUTOSAR OS总共包含以下5大基本对象:Counter,Alarm,Schedule Table,Task,ISRs。这5个基本对象必须归属于一个OS Application,可以简单理解为OS Application是上述5大基本对象的容器。而归属于同一OS Application的基本对象则可以互相访问,来自其他OS Application的基本对象则需要通过配置来限制性访问。
这5大基本对象,OS Application,Core这三者存在着一定的关系,为了更好的理解它们之间彼此的关系,如下图4就较为清晰地描述了者三者之间的关系。
如上图所示,以单核为例,每一个Core可包含1~N个OS Application,而每一个OS Application可包含0~N个基本对象。每个基本对象必须从属于某个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。接下来,将针对这5大基本对象分别展开讲述各个对象的基本特点与用途,以便大家在对OS配置的过程中有一个更为清晰的认识。AUTOSAR OS中存在两种任务:基本任务(Basic Task)和扩展任务(Extended Task)。基本任务则存在以下三种状态:
- 运行状态(Running):处于运行状态的任务可能被高优先级任务或者中断抢占从而进入就绪状态,且同一Core中任何时刻只会存在一个任务处于运行状态,任务运行结束后则将自己挂起进入阻塞状态;
- 就绪状态(Ready): 处于就绪状态的任务由调度器决定是否启动进入运行状态,且该状态时任务切换至运行状态的前提;
- 阻塞状态(Suspend): 处于阻塞状态的任务是被动的,可以由API函数或Alarm激活进入就绪状态;
扩展任务与之相比,则多了一个等待状态(Waiting),解释如下:
- 等待状态(Waiting):当任务的运行需要等待某一或某些事件被置位时,任务进入就绪状态。
基本任务的代码示例如下:
#include “OS.h”
TASK(BasicTask)
{
...
/*User Code*/
...
TerminateTask();
}
扩展任务的代码示例如下
#include “OS.h”TASK(ExtendedTask){ for(; ;) { WaitEvent(Event1); /*Perform some actions in specific condition*/ ClearEvent(Event1); }}
基本任务与扩展任务的状态机切换如下图5所示:
如上图所示,基本任务没有等待状态,所以只能在任务启动与终结时进行同步,基本任务的优点就是占用较小的任务与执行时间。扩展任务则包含多个同步点,没有同步请求的麻烦,当进一步的条件无法满足时,任务则会切换至等待状态,其缺点也很明显,会占用较多的内存和执行时间。Task的符合类(Conformance Class,简称CC)AUTOSAR OS根据不同的软硬件需求,根据每个优先级可能具备的任务个数以及需要的是基本任务还是扩展任务等来定义了四种符合类分别为BCC1,BCC2,ECC1以及ECC2, 各种符合类及属性其如下图6所示:由上表可知,基本符合类BCC1与BCC2仅支持基本任务,扩展符合类ECC1与ECC2基本任务与扩展任务均支持。BCC1与ECC1不支持多次任务激活请求且每个优先级只能有一个任务;BCC2与ECC2既支持多次任务激活请求,同时也支持每个优先级可以有多个任务。各种符合类之间的兼容关系如下图7所示:
AUTOSAR OS是基于优先级进行任务调度,所以每个任务必定有一个优先级,而每个任务都是根据其自身特点来定义一个优先级且需要配置其可抢占属性。可抢占属性可分为不可抢占与全抢占,这里所说的抢占指的是内核抢占。AUTOSAR OS可根据各个任务的可抢占属性配置,来提供不同的调度策略,调度策略可分为以下三种:
- 混合抢占式:OS部分任务是可抢占类型,部分任务是不可抢占类型;
1.对于完全抢占式任务调度策略而言,当前运行的任务可在任何时刻被高优先级任务打断而被迫释放处理器控制权,具备最高优先级的任务从就绪状态转入运行状态,而当前任务被抢占从而进入就绪状态,同时保留现场环境,待下次运行时恢复。如下图8所示为完全抢占式任务调度策略,TaskA为扩展任务,TaskB与TaskC为基本任务,优先级TaskA > TaskB > TaskC。
当前TaskC处于运行状态,当激活TaskB进入到就绪状态时,由于TaskB优先级高于TaskC,所以TaskC被迫释放处理器控制权,调度器开始调度TaskB从就绪状态变为运行状态,直到TaskB运行完成之后,在调度TaskC继续运行。当前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. 若采用非抢占式调度策略,那么当前运行状态的任务在任何时刻都不会其他高优先级任务所抢占,任务的切换只会发生在任务完成时。非抢占式调度策略的问题在于任务执行时间不确定,系统调度实时性较差。如下图9所示为非抢占式调度策略,可见即使高优先级任务 TaskB被激活切换至就绪状态,也必须等到TaskC执行结束之后才能够被调度。
3.若采用混合抢占式,则OS的调度策略就取决于当前任务的可抢占属性,如果为非抢占,则执行非抢占式调度策略,如果为抢占式则执行完全抢占式调度策略。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的激活状况来减少无意义的时钟中断;如下图10所示,则较为清晰的表现了Counter,Schedule Table以及Alarm三者之间的关系。
图10 OS Counter,Schedule Table,Alarm三者之间的关系在计数器的基础上,AUTOSAR OS为应用软件提供了闹钟机制,多个闹钟可以连接一个Counter。当到达Alarm所对应的计数器设定值时,则可以激活一个任务,设定一个event,调用callback或者增加计数器等功能,但只能是一对一。不能像Schedule Table那样,能够在Expiry point同时设定多个Task或者多个Event,这也是为什么引入Schedule Table的原因。一个软件Counter +多个Alarm队列就可以实现静态定义的任务激活机制。但随着Schedule Table的引入,因此一般建议能用Schedule Table就不要用Alarm。如上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): 调度表启动后可反复执行,到达调度表终点后重头开始执行,则每个终结点会被周期性的执行,一般情况下激活任务采用此模式。
如下图11所示,较为清晰了描述了调度表中Expiry Point与Task,Event激活之间的时序关系。
- 每一个终结点必须配置至少一个Task或者Event;
- 每一个调度表至少存在一个终结点(Expiry Point);
- 在每一个Expiry Point优先激活Task,随后设置Event;
在AUTOSAR中定义了两类中断服务程序(Interrupt Service Routine)。分别为一类中断(Category I)与二类中断(Category),两者之间的区别定义如下:
- Category I:此类中断服务程序不能够使用OS提供的系统服务,当中断执行完成之后则会重新跳转至产生中断的地方继续执行,不会影响到任务的执行,因此占用系统资源较少。
- Category II:该类中断则可以调用OS系统服务,如激活任务或者设置事件等。
在AUTOSAR OS中,中断的优先级始终高于任务的优先级,即最低优先级的中断都可以打断最高优先级的任务,即使该任务不可抢占也不例外。因此,中断服务子程序的执行时间不宜过长,否则会影响到整个系统的实时性。Resouce作为OS调度过程中一个十分重要的对象,Resource管理就显得尤为重要。资源管理就是为了协调具有不同优先级的多个任务或者中断对共享内存(如内存或者硬件等)的并发访问。不过幸运的是AUTOSAR OS采用上述的优先级天花板模式来避免任务优先级反转以及死锁问题的发生,即资源的上限优先级必须高于所有该资源的任务以及中断的优先级,但是应低于不访问该资源的任务的最低优先级。其中为了保护共享资源而提出的锁机制-自旋锁(Spin Lock)。该自旋锁一般用于多核操作系统解决资源互斥的问题。当内核控制必须访问共享数据结构或进入临界区时,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了该锁,从而达到某共享资源的互斥作用。
常用函数接口
为了便于大家日常软件调试,我将常见的OS相关API函数接口及其相应功能表述如下图12所示:
由于内容较多,篇幅有限,为防止给大家造成视觉疲劳,有关多核启动与关闭、核间通信、内存与时间保护等精彩内容请听下回分解,敬请诸君多多关注!目前已确认接近50位重磅演讲嘉宾,请点击下面图片,了解会议话题详情