目录
1、AUTOSAR多核操作系统
1.1、OS Application
1.2、多核OS的软件分区
1.3、任务调度
1.4、核间任务同步
1.5、计数器、报警器、调度表
1.6、自旋锁与共享资源
1.7、核间通信IOC
1.8、OS Object中元素交互
1.9、多核OS的启动与关闭
2、多核OS注意事项
2.1、最小部署单元
2.2、核间通信及影响
2.3、注意点碎语
AUTOSAR多核操作系统采用分区机制,多核处理器的每个核中至少分配一个OS应用(OS Application)。每个OS应用均包含任务、中断服务、计数器、报警、调度表等相关要素,统称为操作系统的对象(OS Object)。
每个OS应用需要定义访问权限,AUTOSAR操作系统有两种类型的OS-Application,分别为受信任的OS应用(Trusted OS Application)和不受信任的OS应用(Non-TrustedOS Application)。受信任的OS应用内包含的对象对大部分内存拥有读写的权限,包括所有的Flash, LMU(Local Memory Unit), CSA(Context Save Areas)以及外设单元等,但是没有读取其他非激活栈的权限。非受信任的OS 应用内包含的对象只有读写少数内存的权限,如当前活跃的栈、当前OS应用的数据以及LMU中的共享数据。
受信任的 OS 应用和不受信任的 OS 应用内的对象读写权限如下图所示。以 Corel为例,其中深色阴影部分表示受信任和不受信任OS应用内的对象均有权限读写,浅色阴影部分表示受信任OS应用内的对象有权限读写而不受信任OS应用内的对象无权限读写,白色部分表示二者均无权限读写。
分区(Partitioning)是在硬件层面上对多核操作系统内的部分功能或要素的分割,目的是防止某一个分区中发生的故障影响到同一ECU上的其他分区。在AUTOSAR多核操作系统中,其实现的方法是在内存空间的区域中定义不同的内存区间,在其中执行各自分区的软件代码,防止互相干扰。
在 AUTOSAR OS中,通过 OS 应用对内存进行分区可以实现简单的内存保护功能。一般而言,设计每个OS应用对应一个分区,分区示例如下图所示,无安全等级的任务Task1分配在OS-Application A对应的内存分区Partitionl中,而安全等级相同的任务Task2和Task3、基础软件和复杂驱动都按组分配在OS-Application B对应的内存分区Partition2中,两个分区相互独立。该分区规定了对应的OS应用所能访问的内存地址范围。
受信任的OS应用Os Application_Core0-Nontrusted中,如下图所示,这样,通过将任务映射到不同的OS-Application中,就可以在软件层面实现不同安全等级的任务之间的内存空间以及不具有安全等级和存在安全等级的任务之间的内存空间的隔离。
在任务调度方法上,AUTOSAR 多核操作系统与 OSEK 系统如出一辙,也是根据任务或中断的优先级为首要因素决定的。在同一处理器内核上,优先级越高的任务或中断优先调度;优先级相同的情况下,根据具体的激活顺序决定。在不同的处理器内核上,任务的调度不会互相影响,即AUTOSAR多核操作系统在多核平台上是平行运行的。作为车载多核操作系统,AUTOSAR操作系统中,任务和中断均是静态分配的,在系统运行过程中不可动态迁移,其原因在前文中已有相关解释。
AUTOSAR多核操作系统的任务调度示意如下图所示。根据调度规则,若有多个任务同时被调用,即同时处于就绪状态,则优先级最高的任务率先进入运行状态,如图中内核0中的任务T2,内核1中的任务T3和内核2中的任务T5,三个任务同时进入运行状态。各个内核上的任务独立运行,其优先级没有相互影响。
此外,AUTOSAR多核操作系统支持为任务选择调度模式,分为全调度和拒绝调度两种模式。在全调度模式下,任务在一般的运行状态过程中,会被更高优先级的任务或中断抢占;而在拒绝调度模式下,该任务则不会被任何其他任务或中断抢占。根据上述现象,在任务分配过程中,应用程序中起到关键作用的重要任务应分配较高的优先级,重要程度类似的任务周期越短,分配的优先级应越高。
在多核 AUTOSAR OS中,事件是可以跨核触发的。这也就意味着,核与核之间的同步可以通过事件触发的形式来实现。
如下图所示,当Core0 中任务 R1 执行时,可以通过setEvent来激活位于Corel 中的R2,这个Event可以是变量的写入、读取或者任务执行结束等,使R2可以获得运行的权力,如果Corel中的任务调度允©许,则R2可以获得CPU的执行权力。通过这种方式,可以实现Core0 和 Corel 的任务同步机制。这种方法适用于事件触发的任务或者定期执行的任务的同步。定期执行的任务只需使用Alarm或调度表定期触发Event即可。(事件触发)
下面是使用事件触发来实现多核任务同步的测试。在Core0 中使用 Alarm 定周期触发任务 Core0_Task_1,周期为 1 ms。在 Core0_Task_1 中调用 setEvent 触发 Corel_Task_2.在两个任务中分别翻转引脚 P2.0 和 P2.1。如果两个任务都能正确且同时触发,则 P2.0 和 P2.1 引脚会输出周期为 500 Hz 的PWM 波。测试结果如下图所示,图中上方的波形为 P2.0,下方的波形为 P2.1。
从测试结果中可以看出,Core0_Task_1 和 Corel_Task_2 同时触发,且频率为 500 Hz,符合预期。
还有一种方法是使用调度表来实现任务同步。如下图所示,报警器只能触发一个任务,因此不能实现任务同步。而调度表可以实现同时触发不同核的多个任务。这种方法只使用于定期执行的任务的同步。下图和下下图所示为使用调度表同步出发任务的测试。首先添加配置一个周期为1 ms的调度表,然后使用该调度表分别触发两个核中的任务Core0_Task_1 和Corel_Task_2.在两个任务中分别翻转引脚P2.0和P2.1.
如果两个任务都能正确且同时触发,则 P2.0 和 P2.1 引脚会输出周期为 500 Hz 的PWM 波。测试结果如下图所示,图中上方的波形为 P2.0,下方的波形为 P2.1。
从测试结果中可以看出,Core0_Task_1 和 Corel_Task_2 同时触发,且频率为 500 Hz,符合预期。
AUTOSAR 多核操作系统中,以系统组件中的计数器(Counter)作为任务调度的时间基准,同时,计数器本身也以硬件平台上的时钟(Timer)配置为时间基准,即计数器的参数配置与搭载系统的硬件平台也密切相关。根据上文所述,计数器作为一个应用程序的系统组件之一,是专属于一个内核的,即不同内核上的计数器无法作为不同内核中任务调度的基准。
AUTOSAR操作系统中的任务调度是通过报警器(Alarm)或调度表(Schedule Table)实现的。一般情况下,报警器和调度表均是根据同一内核上的相应计数器触发,特殊情况下,也可以被其他核上的应用程序所调用。如可以在内核0上调用AUTOSAR标准函数SetRealAlarm来设定内核1中报警器的偏移量等。计数器、报警器和调度表三者配合工作形成了AUTOSAR操作系统任务调度机制,如下图所示。
AUTOSAR多核操作系统为实现核间资源互斥,保证数据一致性,设计了自旋锁(Spinlock)机制,该机制不适用于实现核内资源互斥。当某个任务或二类中断成功申请占用自旋锁时,其他内核上的所有任务和二类中断均无法成功申请占用自旋锁,并会处在停滞状态,等待自旋锁占用者将其释放。此时,其他内核仍然处在工作状态,CPU负载率不会下降。因此,对于自旋锁的使用应当谨慎,执行时间较长的任务不宜申请占用自旋锁,以防止对于其他内核的资源浪费。若对自旋锁的使用比较复杂,还可能导致不合理的死锁状态。根据上述现象,处理器内核之间的通信或共用数据应尽可能减少,耦合性较强的任务应被分配至同一核中。如下图所示,一旦低优先级的任务在占用自旋锁的情况下被高优先级任务抢占,如果高优先级任务也需要申请占用同一个自旋锁,根据自旋锁机制的设定,其必须等待低优先级任务将其释放。而根据AUTOSAR操作系统任务调度机制,高优先级任务在运行过程中不可能被低优先级任务打断。故此时内核0陷入了死锁状态。AUTOSAR操作系统为了防止上述情况发生,在任何任务或中断占用自旋锁时,操作系统会自动挂起所有中断,即某任务成功申请自旋锁占用之后,不会被同一内核上的任何任务或中断抢占。
如下图所示,内核0上的某任务在运行过程中,需要先申请占用自旋锁A,再申请占用自旋锁B;同时,在内核1上的某任务,运行过程中需要先申请占用自旋锁B,再申请占用自旋锁A.如果两个任务在相近的时刻开始运行,则很有可能发生自旋锁A被内核0上的任务占用,自旋锁B被内核 1 上的任务占用,此时,两个内核上的任务相互锁死,即自旋锁的嵌套调用导致的死锁。
为了防止该现象的发生,需要相关技术人员在进行系统设计时,禁用嵌套使用自旋锁的请求或者严格按照顺序嵌套自旋锁的方式进行请求。由下图可知,顺序地访问自旋锁不会造成死锁,而形成回环访问时,会产生死锁现象。根据上述规则,在进行系统设计时,任务对自旋锁的申请占用应尽量避免嵌套和回环,以避免造成可能的死锁现象。
回环访问SpinLock造成的死锁
AUTOSAR多核操作系统为实现核内资源互斥,保证数据一致性,设计了共享资源(Shared Resource)机制,该机制不适用于实现核间资源互斥。当某个任务或二类中断成功申请占用该内核的共享资源时,该内核上的所有任务和二类中断均无法成功申请占用该共享资源,并会从运行状态返回等待状态,等待共享资源占用者将其释放。
与自旋锁不同的是,共享资源的申请者在发现共享资源正在被占用时,会返回等待状态,不会发生锁死现象。而且根据之前文章所述的机制,AUTOSAR多核操作系统继承了OSEK操作系统中的优先级天花板模式,以此防止任务触发的优先级顺序出现的可能错误。
AUTOSAR 规范定义了包括核内通信、核间通信和外部通信在内的三种类型的通信方式,其中前二者统称为内部通信。每当不同内核中的应用程序需要进行数据传输时,操作系统会为其开辟共享的内存Cache区域,通信双方的应用程序通过对该区域的读写,完成数据的传输。但Cache区域的数据有可能在被读的过程中发生更新,导致数据的不一致性。
为了解决上述问题,AUTOSAR多核操作系统提供了应用于核间通信的IOC(Inter OSApplication Communication)方式。IOC不同于核内通信,核内通信是在运行时环境层完成,而1OC是在操作系统中完成的,如下图所示。应用程序在对上述共享Cache区域进行读写时,会申请占用一个自旋锁,以防止其他内核上的应用程序同时访问。由于通常情况下IOC不涉及自旋锁的多次调用,故在本书的研究中,可以采用 Vector MicroSAR 提供的迷你自旋锁(Minilock)机制。迷你自旋锁的功能与自旋锁一致,只是占用的系统资源更少,执行时间比自旋锁更短,且配置更容易,有利于减少IOC通信所需的时间。
根据上述内容,为保证数据的一致性,同时尽可能缩短核间通信的所需时间,当某任务访问共享数据区域时,可以采用迷你自旋锁机制。
AUTOSAR 多核操作系统由任务、中断、报警器和事件等元素组成,它们之间的相互作用使操作系统有序地运行。广义上,AUTOSAR OS是事件驱动的操作系统,Alarm可以视为报警事件,如下图所示。
综合上图和之前章节的内容可知,一个任务可以由报警器、调度表、其他任务或中断激活,事件可以由报警器、调度表、任务或中断置位。当某一任务或中断获得共享资源时,共享资源被加锁,其他任务或中断不可访问。
在AUTOSAR软件架构下,无论是单核操作系统还是多核操作系统,都与EcuM(ECUState Management)和 BswM(BSW Mode Management)两个管理模块息息相关,它们控制着操作系统启动、初始化、运行、关闭等状态及其过程。AUTOSAR操作系统的启动与关闭的过程如下图所示。
由下图知,ECU工作在启动(STARTUP)、正常运行(UP)、睡眠(SLEEP)和关闭(SHUTDOWN)四个状态。ECU在上电前处于SHUTDOWN状态,上电后进入STARTUP 阶段,它包含 StarPreOS 和 StartPostOS 两个阶段,StartPreOS 阶段对 ECU 进行一些准备工作以初始化 OS,其主要动作如下:Callout:设置可编程中断的优先级;Callout: EcuM_AL_DriverInitZero 对没有配置后期编译(Post-build)参数的 BSW 模块进行初始化,该部分代码为手工添加;
Callout:调用函数 EcuM_DeterminePbConfiguration(),返回指向 Post-build 数据的指针;
检查数据配置的一致性,若出现错误,则调用 EcuM_ErrorHook;Callout: EcuM_AL_DriverInitOne 初始化 Mcu, IO, GPT, Watchdog 等模块;
获知复位原因;
选择默认的 Shutdown 对象;
Callout:调用EcuM-LoopDetection,检查ECU是否被循环复位;启动 OS。
StartPostOS 阶段就是 OS 启动后的阶段,该阶段主要执行初始化 BSW 的调度器和初始化 BswM模块两个动作。综上所述,启动阶段的流程如下图所示。
如上图所示,ECU上电后首先执行微控制器的启动引导程序(BootMenu),然后执行Cinit,对栈进行分配,之后,EcuM接管控制权,执行StartPreOS,在该阶段,EcuM执行Callout代码段,启动OS,此时,OS接管控制权,执行OS启动及其回调函数,启动OS需要自动启动的任务,通常为初始化任务,调用EcuM_StartupTwo();EcuM重新接管控制权,执行 StartPostOS完成 ECU 和 OS 的启动,OS 中的其他任务被允许执行。ECU的控制权被转移至 BswM。无论是否存在OS,多核的启动和硬件紧密相关,通常情况下,硬件会启动一个核作为主核(Master Core),而从核(Slave Core)由软件启动,这种方式被称为主从模式(Master-SlaveStartup Behavior)。 AUTOSAR规范定义了多核OS的启动为主从模式。多核OS的具体启动流程如下图所示。
多核OS启动过程
与OS 的启动类似,OS的关闭也是由EcuM完成。若在关闭过程中有唤醒事件出现,则ECU在关闭后立刻重新进行启动。在关闭过程中,系统会选择Shutdown Target,Shutdown Target包含关闭(Off)、睡眠(Sleep)和复位(Reset)。关闭ECU的具体过程如下图所示。
在多核系统中,目前AUTOSAR 4.x不支持只关闭单个核,即若关闭指令发出或者致命错误出现时所有核必须全部关闭。关闭 OS 的具体过程如下图所示。
如上图所示,若某一任务拥有调用 Shutdown All Cores 的权限时,关闭信号会被发送至所有核。当关闭过程启动后,所有的中断服务和任务都不会被激活,关闭前必须完成的程序由 EcuM 保证完成。关闭完成前,由 OS Application Shutdown Hooks 完成相应的回调程序,然后等待至同步点所有核执行关闭回调程序。
在单核操作系统进行runnable到task的映射时,最小的分配单元是一个runnable,而且基本上都是同一个周期的 runnable 分配到同一个task 中,减小任务切换带来的额外负载。而具体到AUTOSAR规范的多核操作系统,它的分配最小单元不是runnable,而是SWC(如下图)。一个SWC是实现某个特定功能的逻辑概念,实现这个逻辑功能的实体是若干个runnable。因此,这也就意味着,同属于一个SWC的runnable需要分配到同一个核中执行。
在前文中,介绍了一种由单核应用部署过渡到多核操作系统的方法。这种方法的思想就是通过分析同一个task中各个runnable的数据流情况,根据数据流的走向,将runnable分配到多个核中。它的主要原理是使数据流出现分支时,各个分支可以分到多个核中,以减少每个task的最坏情况执行时间(WCET)。这种方法具有简单易行的特点,在单核操作系统上运行的任务集可以直接往多核操作系统平台上转换,不必再花费大量的精力去验证软件在多核操作系统上的稳定性。但是,这种方法的最小分配单元还是 runnable,如果数据流分支处于同一个SWC中,这种方法将无法发挥其作用。如果需要采用这种方法进行多核应用的部署,需要在 runnable映射的同时对 SWC 进行拆分重构,将原来功能上的 SWC 进行分解,从部署角度重新构建 SWC。这种方法虽然在流程上不符合AUTOSAR规范,但是,在实现的过程中可以有效地减少WCET,达到降低CPU负载的作用。软件组件(SWC)的部署如下图所示。
根据上文所述,实现对核间负载平衡基础就是通过对SWC的核间分配,最小的分配单元是一个SWC而非runnable,因此,以此种方法进行多核任务的部署不需要重构SWC,只需在ECU配置阶段直接映射即可。
如果多核操作系统之间不存在任何的通信,则多核操作系统就相当于多个独立运行的单核操作系统。但是,往往部署到多核操作系统上的应用需要进行相互间的信息交换。比如多个核共用同一套硬件外设资源,在Vector的 AUTOSAR 软件包中,当需要与底层的外设打交道时,非主核不能直接操作硬件抽象层的资源而必须借助核间通信IOC与主核通信,从而控制底层的硬件驱动。下图和下下图所示的是Vector MICROSAR的主核AUTOSAR架构以及多核操作系统架构。
其中Microcontroller Abstraction Layer为微控制器硬件抽象层。Core0为主核,Corel为从核,Core0通过输入输出硬件抽象层(IOHWAB)调用微控制器抽象层的服务。而Corel需要控制微控制器抽象层时,则不能直接调用其服务,而是需要借助IOC与Core0进行通信,取得微控制器抽象层的服务。又如位于两核中的 SWC 需要进行数据的交互时,同样需要借助IOC进行核间通信。
核间通信的出现,使得原本独立的各核有了相互之间的影响。这种影响主要由核间通信引起的通信耗时对核上负载率的增加。通过对多核处理器硬件架构的分析以及IOC通信机制的分析可以知道,通信耗时主要由两部分组成:访问硬件资源时由硬件架构引起的额外耗时以及在 IOC 通信机制内使用 Spinlock 引起的同步耗时。Runnble的运行时间大概可以分成计算时间、任务切换时间和通信时间三个部分,如下图所示。计算时间是指在正常的逻辑运算所需要的时间,任务切换时间是指操作系统根据任务的优先级安排就绪任务运行时所需要耗费的时间,而通信时间是指将数据从内存中读出或者写入完毕所需要的时间。由于硬件的不同,读入或者写入单位数据的耗时不同,而且需要从数据一致性的角度出发使用spinlock,当多个核在一段时间内都需要访问同一个硬件资源时,就会存在冲突,访问的顺序需要根据发起访问的时间和发起访问的核的优先级来确定,当核间通信的数据量大且频繁时,特别是对小周期的任务来说,将会对CPU的负载产生明显的增加作用。因此,在进行多核间任务的分配时,需要特别注意减少核间通信的数量及其频次,从而降低核间任务的耦合性及 CPU 运行的负载。
另外,核间通信需要确保数据的一致性。数据的一致性,是确保应用程序能够正常执行且结果正确的必要条件之一。所谓数据的一致性,就是保证在程序执行的过程中,其值不会因为被中途篡改或者数据更新不及时而导致程序执行的结果与期望不一致。在AUTOSAR中,核间通信都需要通过IOC通信机制来进行,以保证核间通信的数据一致性。
SWC是应用层中不可分的原子单元,可作为运行实体的载体。
软件组件之间通过端口实现连接,端口之间通过虚拟总线 VFB 连接,从而实现逻辑上的数据交互。根据通信方式的不同,可将端口分为S/R(Snder/Receiver)端口和C/S(Client/Sever)端口。S/R 用于数据的发送与接收,支持1:n 和n:1 的数据传输,可传输多个数据元素。既支持简单数据类型的数据传输,如整型、浮点型,也支持复杂数据类型的传输,如数组、结构体。当数据传输经过总线时,数据元素需映射至信号。该种接口的C代码体现为公有变量,故该类型的端口需要分配一个或多个数据元素(Data Element)。C/S用于服务的调用,Client发出请求后,由Sever进行数据处理,输出量送达Client,从而实现功能模块的相互调用。该类型端口支持 1:1 和 1:n 通信,支持同步和异步通信,支持ECU 内和跨 ECU 通信。该种接口的C代码体现为函数的接口。此外,AUTOSAR还提供标定端口、自定义模块端口等其他类型的端口。端口的创建方法如下图所示。
AUTOSAR 引入运行实体 RE(Runnable Entity)的概念来描述程序的具体执行单元,在C代码中的体现为函数。在应用层,运行实体被分配至软件组件中;而在基础软件层,运行实体被分配至任务中。每一个运行实体都具有一个或多个触发条件,RTE根据触发条件对运行实体进行调度,常见的触发事件有数据接收、超时、函数调用等。运行实体具有一个或多个端口,用来表示运行实体的端口访问权限。运行实体的设计界面如下图所示。
数据类型和数据映射是为方便应用层数据操作的定义。AUTOSAR提供三类数据类型:应用数据类型(Application Data Types)、具体实现的数据类型(Implementation DatalTypes)和基本数据类型(Basic Data Types)。应用数据类型有语义无语法,是为方便上层软型件设计而定义的,具有望文生义的功能,它必须映射到一个具体实现的数据类型。具体实现的数据类型既有语义又有语法,在C代码中会生成相同名称的变量,该类数据类型可从库中选择也可由基本数据类型创建并由分配计算方法和数据约束。基本数据类型是有语法无语义的,不可以被操作,主要用于创建具体实现的数据类型。数据映射是指应用数据类型到具体数据类型的映射。数据类型的创建界面如下图所示。