1 前言
如表1所示,OSEK提供了一些特殊的钩子例程(Hook routines),应用层可以在钩子函数中自定义操作,以参与到操作系统的内部处理中。
钩子函数例程 | 功能用途 |
ErrorHook | 用于错误处理 |
StartupHook | 在系统启动后,调度器运行前调用(is called after the operating system startup and before the scheduler is running.) |
ShutdownHook | 当应用层请求系统关闭,或OS自身因为严重的错误要关闭系统时,调用该钩子函数 |
PreTaskHook | 上下文切换过程中,新任务进入运行态那一刻调用 |
PostTaskHook | 上下文切换过程中,老任务退出运行态那一刻调用 |
这些钩子函数例程有如下特点:
• 钩子函数由OS调用,其调用的上下文取决于OS的具体实现,即完全由OS决定;
• 优先级比所有任务都高;
• 不会被二类中断(category 2 interrupt routines)打断;
• 是操作系统的一部分,但由应用层来定义实现其具体操作内容;
• 在接口层统一标准,但功能层(environment and behaviour of the hook routine itself)取决于具体实现,因此通常不具有移植性;
图1 API调用限制说明
由图1可见,大多的系统API服务都不可在钩子例程中使用,这一限制主要是为了降低系统的复杂度。如果应用函数在钩子例程中违规调用了不可用的API服务,其行为是未定义的,但OS应返回特定的错误码。
2 错误处理(Error handling)
OSEK提供了一种错误处理服务(error service),用于处理OS的临时错误或持续性错误(temporarily and permanently occurring errors)。也就是说,ErrorHook是用来处理OS内部检测到的错误,这些错误的直接体现就是各系统服务API的返回值。
2.1 错误分类
OSEK将错误分为两类,应用错误(Application errors)和致命错误(Fatal errors),其对应的处理策略也各不相同。在错误处理的过程中,这些错误处理服务都会获得对应的错误参数,以明确错误信息。
对于应用错误来说,其主要表现是OS对应的系统服务的执行出现了错误,但OS的内部数据结构仍保持正确性和完整性。在这种情况下,OS会先进行集中式的错误处理(centralised error treatment),并向通过处理状态返回错误信息,由应用层根据错误类型决定后续操作,即所谓的分散化处理(decentralised error treatment)。
对于致命错误来说,OS无法在保证其内部数据结构的正确性和完整性,诸如任务控制块、任务控制列表等。在这种情况下,OSEK会选择人死帐销,直接调用集中式系统关闭。
OSEK将错误检查分类两类,标准状态和拓展状态(standard status and extended
status)。如果一个任务是是在标准状态下激活的,则可以返回"E_OK" 或 “Too many task
activations”两种状态;而拓展状态在此基础上,还可以返回"Task is invalid" 或 "Task still occupies resources"等状态。
此外,对于OSEK来说,系统调用API的返回值的优先级高于其输出参数。也就是说,当返回值为错误时,其对应的输出参数是未定义的。
2.2 避免ErrorHook的递归调用
当一个系统调用的返回值不为E_OK时,就会调用错误处理钩子例程(ErrorHook)。特殊地,为了防止ErrorHook的递归调用,OSEK规定,如果发生错误的该系统调用来自于ErrorHook内部,则错误发生时,不会触发对ErrorHook的(递归)调用。
因此,在ErrorHook内部,如果进行系统API的调用,其错误检查的唯一方式就是判断该系统调用的返回值。
2.3 错误处理
OSEK为ErrorHook的错误处理制定了标准规范,以方便获取错误时的上下文信息,包括发生错误时的系统调用API及其对应调用参数,具体逻辑架构如图2所示:
图2 Example of centralised error handling (extended status)
其中,宏OSErrorGetServiceId()提供发生错误时所调用的系统服务ID(service identifier),其对应的类型为OSServiceIdType,取值通常为OSServiceId_xxxx(xxxx为系统服务的名字)。
关于系统服务的参数获取,也有标准的命名规范,通常统一为OSError_Name1_Name2:
• Name1: is the name of the system service
• Name2: is the official name of the parameter within the OSEK OS specification
举例来说,当触发ErrorHook的系统服务为SetRelAlarm时,SetRelAlarm对应的参数通常可以通过以下宏定义获取:
• OSError_SetRelAlarm_AlarmID()
• OSError_SetRelAlarm_increment()
• OSError_SetRelAlarm_cycle()
2.4 系统启动(System start-up)
通常来说,处理器复位后,系统初始化的过程取决于具体的实现,但OSEK规定了一个标准的初始化流程,包括硬件初始化、OS初始化及应用的初始化。
OSEK并未强制应用程序在系统初始化完成后定义特定的任务,但允许指定一些自启动任务或自启动定时器(autostart alarms)随着系统初始化一起启动。
图3 System start-up
系统的启动过程如图3所示:
① CPU复位后,首先进行的时硬件的初始化,初始化完成后会进行应用模式的判别(application mode),处于安全考虑,该判别不可依赖于系统历史记录(system history);
② 随后就是可移植的部分了,标准接口StartOS会根据当前的应用模式进行OS的初始化;
③ OS进行一系列的初始化操作;
④ 调用StartupHook,该钩子函数的实现由应用层实现,主要用于进行一些和OS强相关的一些初始化操作,应用层可以通过GetActiveApplicationMode获取当前的应用模式,在此过程中,所有中断都被禁用;
⑤ 钩子函数运行完毕后,OS会使能所有中断,启动调度器,并先后激活当前应用模式下定义的自启动任务和自启动定时器,最后,系统开始运行,开始按调度规则启动应用层定义的任务。
2.5 系统关闭(System shutdown)
OSEK定义了一个标准接口ShutdownOS用于系统的关闭。该接口通常由应用层调用,请求关闭系统,或者由于OS探测到致命错误时,主动调用。
当ShutdownOS被调用时,OS会调用钩子例程ShutdownHook函数,并待其执行完毕后,才真正关闭系统。
2.6 系统调试(Debugging)
PreTaskHook 和 PostTaskHook用于在任务切换的上下文中调用,具体如图4所示。这两个钩子例程函数主要用于系统调试或时间测量(time measurement),包括上下文切换时间。
因此,PostTaskHook会在老任务离开运行态之间被调用,而PreTaskHook 则是在新任务进入运行态那一刻被调用。由于调用时新老任务都仍旧/已经(still/already)在运行态,整个过程中,系统服务GetTaskId都是可用的。
图4 PreTaskHook and PostTaskHook
若是被ShutdownOS调用时,有任务正在运行,则ShutdownOS可以调用PostTaskHook,也可以不调用PostTaskHook,且OSEK对PostTaskHook和ShutdownHook运行的先后顺序没有做强制要求。