引言
为了满足日益庞大、复杂的汽车电子控制软件的开发需要,增强应用软件的可移植性和不同厂商的控制模块间的可兼容性,1993年欧洲汽车工业界联合推出了OSEK/VDX(Open Systems and the Corresponding Interfaces for Automotive Electronics,汽车电子的开放式系统及接口)标准[1],简称“OSEK标准”,旨在为汽车上的分布控制单元提供一个开放结构的工业标准。OSEK标准从实时操作系统、软件接口、通信和网络管理等方面对汽车的电子控制软件开发平台作了较为全面的定义与规定。该规范为集中的操作系统子规范定义了一个小的、可伸缩的实时操作系统,对于存储容量有限和功能专用的嵌入式系统是非常理想的。从具有8 KB ROM到512 KB ROM的微处理器上均可使用这样的嵌入式操作系统,该操作系统可管理实时任务,强化定时器功能,共享资源等[2]。
本文主要介绍了一种遵循OSEK规范自主设计开发的嵌入式实时操作系统AutoOSEK,详细讨论了该操作系统相关模块的设计思想和实现方法,然后进行了相关性能测试,并结合测试结果对其性能进行了分析。
1 AutoOSEK系统体系结构
AutoOSEK在设计时,按功能需求采用模块化设计思想,并参考了μC/OSII的设计方式,采用硬件无关部分与硬件相关部分完全独立的设计架构。与硬件无关部分的代码,采用移植性很强的ANSI C编写;与硬件相关部分的代码,采用汇编语言编写,并且压到了最低限度,从而最大程度上降低了移植的复杂性。
AutoOSEK将操作系统按功能要求分为:任务管理和调度、事件机制、资源管理、消息机制、报警和计数管理以及中断服务程序和中断管理等模块。AutoOSEK系统体系结构如图1所示。其中任务管理和调度为核心,其他管理机制为它提供不同的服务支持。报警管理为任务提供了定时和循环处理机制;事件机制为扩展任务提供了同步机制;资源管理用来实现不同任务间互斥访问共享资源;消息机制用于实现任务间通信;中断服务程序和中断管理为以上管理机制的实现提供服务。
图1 AutoOSEK系统体系结构
2 AutoOSEK设计与实现
按照上述的体系结构,下面分别从任务管理和调度,系统采用的调度机制,以及为其服务的事件机制、资源管理、消息机制和报警机制等方面来讨论AutoOSEK的具体设计与实现。
2.1 资源管理
OSEK的资源可以是一段临界区代码、调度程序、共享内存或数据结构,也可以是共享硬件设备。大多数多任务/多进程的操作系统都提供了在任务或进程间共享资源的方法,典型的方法有信号量和互斥,这些方法的缺点是可能引起优先级反转或死锁。
为了防止优先级反转和死锁的发生,OSEK标准提供了优先级天花板协议(Priority Ceiling Protoco1,PCP)。如同任务一样,将系统中用到的每种资源在初始配置时定义一静态优先级。PCP协议如下[3]:
① 资源的优先级下限高于或等于访问这个资源的所有任务中最高的优先级。
② 资源的优先级上限低于所有不访问该资源且优先级高于在①中出现的最高优先级的任务的最低优先级。
③ 当任务对资源加锁时,任务的优先级暂时被设为该资源的优先级。
④ 当任务对资源解锁时,任务的优先级还原为它定义的静态值。
AutoOSEK完全实现了PCP协议。即当一个任务占用了一个资源后,该任务的优先级会临时升高为该资源优先级。其优先级为可能使用该资源的所有任务的优先级的最高值。这样,该任务只会被不使用该资源并且比该资源优先级高的任务强占,直到它释放该资源为止。因此,当一个任务试图占用一个资源时,不可能有任何其他任务正占用着该资源,也就不会有因试图占用资源而进入等待态的进程。同时,该协议中不存在等待任务,自然也就避免了死锁。
2.2 任务管理和调度
在AutoOSEK操作系统中,任务管理能力相对有限,这是因为系统的任务设置在系统生成时已经定义好了,并且系统中任务的数量保持不变,不允许动态创建和删除进程。OSEK标准把任务分为基本任务和扩展任务。基本任务状态包括就绪态、运行态和挂起态[4],任务切换只发生在这3种状态之间;扩展任务除了具有基本任务的3种状态外,还有等待态,并支持事件机制。基本任务和扩展任务各自的状态间切换如图2所示。
图2 两类任务切换图
OSEK规范定义了4种有效的“符合类”,分别为BCC1、BCC2、ECC1、ECC2。一个符合类被定义为操作系统要求的一个具体实现,这些要求包括由应用程序指定的属性集。其中,BCCx符合类只支持基本任务;ECCx符合类支持基本任务和扩展任务;xCC1符合类要求同一优先级只有一个任务;xCC2符合类允许不同任务占用同一个优先级。
由于xCC1符合类要求同一优先级只有一个任务,因此在调度时可按任务优先级调度,实时性和可靠性很高。而为满足xCC2符合类的要求,需为每一优先级设置一个保存该级别下所有任务的队列。在调度时需首先根据优先级查询出当前就绪的最高优先级任务队列,然后再在该队列中采用时间片轮转等算法获取将要运行的任务,因而复杂性高,CPU占用率高,任务切换时间较长,实时性和可靠性较差。AutoOSEK在实现时采用的符合类级别为ECC1,支持基本任务和扩展任务,满足了汽车电子控制系统设计的要求[5]。
操作系统中的任务同步通过任务调度来实现。OSEK规范支持3种调度策略:非抢占任务调度、全抢占任务调度和混合抢占任务调度。其中,混合抢占任务调度既可将一任务表示为非抢占型,也可表示为抢占型。AutoOSEK完全实现了这3种调度策略,并且可通过共用内部资源方式定义任务组,使一组任务能同时具有抢占或非抢占性的特征,这一设计增强了不同应用选择任务调度策略的灵活性。
2.3 警报和计数器
OSEK规范提供了报警机制,报警基于系统时钟,或者其他的某种计数器,当计数器到达报警设定值时被触发。报警触发后可以激活进程,也可以为某一进程设置事件,或者执行一个报警回调程序,具体由用户在系统生成时静态定义。但报警值是动态设置的,可以是相对值或者是绝对值,也可以设为循环报警来激活周期性进程。
报警是与计数器关联的系统对象。当定义报警后,每个报警被静态地赋予一个计数器和一个任务,多个报警也能被赋予一个给定的计数器。每当一个计数器递增时,赋予这个计数器的当前活跃报警与计数器的值比较。如果值相等,则报警被触发并且可进行下面两种动作之一:激活一个任务,或设置任务的一个事件。AutoOSEK中将报警服务分为绝对报警和相对报警服务。二者唯一区别为报警值的定义,前者为相关计数器的绝对值,后者为相对值,与设置报警时相关计数器的当前值有关。
2.4 事件机制
事件机制为OSEK操作系统提供的另一种同步机制。该机制的含义是,一个处于等待状态的扩展任务,只有当它所等待的事件至少有一个发生时,才能进入就绪态,并且事件的发生会以信号的方式传给该任务。事件机制可用于多个任务的同步,同时也是任务内部通信的方法之一[6]。虽然只有扩展任务才可以等待事件,但设置这些事件的却可以是任何任务或中断服务程序。有一点要注意,为了遵循占用了资源就不被阻断的原则,必须避免一个占用了资源的任务因等待事件而进入等待状态。
事件机制为OSEK标准中的一种重要同步机制,只应用于扩展任务。事件是实现扩展任务在等待状态和就绪状态间转换的标志。AutoOSEK中事件管理实体采用TCB中的双掩码结构OSTCBSetEvent和OSTCBWaitEvent,掩码的每一位对应一个事件,事件具体含义由用户定义。图3为扩展任务通过事件机制取得同步的流程图。
图3 扩展任务通过事件机制取得同步流程
2.5 消息机制
在AutoOSEK环境下,任务之间通过消息实现通信,I/O也使用这一系统。消息是应用数据的容器,只有一个发送者,但可以有多个接收者。消息类型必须在系统创建时定义,不能在运行时增加和删除消息。
AutoOSEK支持两种消息类型:静态长度和动态长度消息。前者的长度在系统创建时设置,不能在运行时改变。后者的长度可以在运行时改变, 但需要在系统创建时指定最大长度。操作系统还将消息分为可排队和不可排队的。前者是静态长度消息,内部数据结构被组织成FIFO队列,能被接收服务例程移走。不可排队消息是不断被刷新的消息,不能被接收服务例程移走。
2.6 堆栈空间分配
AutoOSEK主要应用在汽车电子领域,对内存占用大小要求较严格,在设计时堆栈管理按以下方法做了优化。
由于包括Motorola HCS12在内的很多微处理器硬件本身不自动提供中断堆栈机制,设计时专设一中断堆栈区。当系统由任务态切换到中断服务程序时,先在任务堆栈区中保存上下文,然后堆栈指针指向中断堆栈区,当中断服务程序执行完毕返回到任务态时,堆栈指针再重指向任务堆栈区并恢复保存的上下文。这一设计无需在分配每一任务堆栈区时为中断服务程序预留堆栈存储空间。设中断堆栈区需占用空间为M字节,应用系统中定义了N个任务,则采用这一设计方法可使堆栈空间减小(N-1)×M字节。这一设计方法可运用到基于任何硬件本身不自动提供中断堆栈机制的处理器中的实现。
在对内存占用大小要求严格的环境下,应用程序开发者可根据各任务和中断服务程序代码情况来定义各堆栈区的空间大小。在分配中断堆栈区时需充分考虑AutoOSEK支持中断嵌套特性;同时,系统提供了堆栈检测机制来方便应用程序开发者调试,从而保证了各堆栈区空间大小的合理分配[7]。
通过以上设计方法,AutoOSEK优化了堆栈管理。特别是针对今后在汽车仪表上的应用,由于汽车仪表系统相对汽车控制系统而言,实时性要求较低,很多任务可按预先定制好的顺序进行执行,因此在设计时可将这些任务设计成非强占调度方式,从而通过上述堆栈分配机制更好的优化了系统运行时所需的空间,可以满足硬件资源有限的环境下系统稳定运行的要求。
3 AutoOSEK系统的测试
AutoOSEK测试主要包括功能测试和性能测试两个方面。其中,功能测试主要测试系统对OSEK规范要求的各服务实现程度;性能测试主要测试系统的可靠性、实时性、占用空间、移植性以及配置和扩展性。
3.1 功能测试
根据AutoOSEK系统的设计要求,主要检测OSEK OS规范中符合ECC1符合类以及OSEK COM规范中内部通信部分要求的各API服务是否完全实现。按功能将系统分为任务管理,中断处理,消息机制,事件机制,资源管理,报警、出错处理6个模块进行测试。在测试时为充分覆盖各API服务和各种情况,在编写测试用例时对每一模块采用分类树的方法,从而对每一分支按OSEK规范要求逐一进行测试。
以任务管理和调度模块为例,其分类树如图4所示。
在图4中,测试用例1表示系统调用ActivateTask服务并传递无效的TaskId时,系统返回值为E_OS_ID。测试用例2表示全抢占方式下,系统调用ActivateTask服务时,当要激活的任务已经就绪时,系统返回值为E_OS_LIMIT。
按以上方式对每一模块进行功能测试,结果表明: AutoOSEK系统实现了OSEK OS规范中符合ECC1符合类以及OSEK COM规范中内部通信部分的功能要求。
图4 AutoOSEK系统任务管理和调度模块分类树
3.2 性能测试
(1) 移植性
AutoOSEK系统采用硬件相关代码与硬件无关代码完全独立的设计方法,只要该处理器有堆栈指针和CPU内部寄存器入栈、出栈指令就可以移植到AutoOSEK系统,从而保证系统便于移植。现已经通过仅修改系统中与硬件相关部分代码,完成了在C8052F120 (8位处理器)、HCS12(16位处理器)和AT91RM9200(32位处理器)上的移植,从而证明系统支持从8位到32位处理器的移植。
(2) 占用空间
AutoOSEK内核在上述3种微处理器中编译成可执行文件均不超过8 KB,满足了系统在硬件资源有限的环境下的实现。
(3) 任务切换时间
AutoOSEK在国家软件中心进行测试时,相关测试报告对各种情况下任务切换时间的描述如表1所列。
表1 任务切换时间
(4) 配置和扩展性
AutoOSEK在系统生成时,可根据具体要求灵活配置任务符合类,调度方式,定义包括任务、资源、事件、消息、中断服务程序和报警等系统对象的各属性和相互之间的作用关系,从而保证系统具有良好的可配置性和扩展性,可以根据应用的要求进行装卸和扩展。
结语
本文首先介绍了OSEK标准,阐述了基于OSEK标准的嵌入式实时操作系统AutoOSEK的设计与实现,并对AutoOSEK系统的功能和性能进行了测试。
在系统设计中,充分考虑了内核本身的可移植性;采用了硬件无关部分与硬件相关部分完全独立的体系结构;完成了任务管理和调度、事件机制、资源管理、消息机制和报警机制等与硬件无关部分的实现,以及中断服务程序和中断管理等与硬件相关部分的实现;并分析了为满足系统可稳定运行在硬件资源有限的环境下,采用的优化堆栈分配机制。
通过系统的功能测试,系统基本实现了遵循OSEK OS标准ECC1符合类和OSEK COM标准中内部通信部分的功能。通过系统在可靠性、实时性、移植性、占有空间、可配置性和扩展性方面的性能测试表明: 系统具有较好的可靠性;系统任务切换时间较小,有较好的实时性;系统已完成从8位到32位芯片的移植,具有很好的移植性;系统占用空间小于8 KB,满足硬件要求苛刻的环境;配置灵活,扩展性强。