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