1 OSEK中断分类
如图1所示,OSEK将中断服务函数(Interrupt Service Routine,ISR)分为两类:
① 一类中断服务函数(ISR category 1)
该类ISR不会使用OS提供的系统服务(不包括那些中断使能除能的相关接口,毕竟OS只是封了层皮,本质上还是调用MCU提供的指令接口,一般是通过修改一些中断屏蔽寄存器来实现),中断处理完成后会返回程序被打断的指令处执行。
显然,一类中断与任务管理无关,与OS井水不犯河水,开销也比较小。换句话说,一类中断完全生活在OS层以下,OS完全感知不到它的存在。
② 二类中断服务函数(ISR category 2)
二类ISR完全生活在OS的统治下,由OS为用户层例程(user routine)提供中断服务帧(ISR-frame),作为运行环境。在系统生成(初始化)时,该用户例程会被分配给中断,用于中断处理。
简单来说,二类中断的ISR的触发虽还是由硬件控制,但其ISR必须由OS来指定,多了个话事人。
图1 OSEK ISR分类
而在freeRTOS中,以宏变量configMAX_SYSCALL_INTERRUPT_PRIORITY为分界线,将外部中断通过优先级分为两类,较低优先级的中断可以使用OS的接口,反之则不可用。其主要原因就是,OS的很多临界区保护的实现都是通过基础优先级寄存器(BASEPRI),以提高系统可执行优先级来实现的(屏蔽了低优先级的中断请求),以至于OS的接口不可在优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断中使用。
总的来看,OSEK和freeRTOS中的中断分类方法应该是师出同门,在关于系统服务的使用上步调一致,但又都自成一家。
2 OSEK中断行为分析
在ISR执行时,不会发生任务重调度;但当二类中断执行完毕且当前无中断发生的情况下,如果有有高优先级任务被唤醒(进入就绪态),则退出中断后即会触发重调度(rescheduling)。
简单来说,二类中断的中断返回处是一个系统调度点,OS会检查是否有高优先级任务抢占当前的较低优先级任务;如果发生任务抢占,则进行任务上下文切换。
由于中断优先级的最大级数以及中断的调度(激活、抢占等)等机制,都与硬件实现强相关,OSEK并未对其有明确的规定和限制。也就是说,中断和任务都可以通过优先级来执行抢占策略,但中断的调度是由硬件保证的,而任务的调度是由OSEK来执行的。
显然,中断的优先级要比任务高;如果中断种激活了一个任务(使之进入就绪态),任务的调度(使该任务进入运行态)则是在所有中断处理完成后,由OSEK来执行的。
3 中断服务函数(Interrupt service routines,ISR)
3.1 中断嵌套(Nested interrupts of different categories)
如前文所述,中断的优先级始终高于任务。通常来说,可以在二类中断种调用系统接口,如ActivateTask()或SetEvent()触发OS的重调度(新唤醒的任务优先级比当前运行态的任务优先级更高的情形下)。
另一方面,当中断嵌套的情形发生时,尤其当二类中断抢占了一类中断时,如果在二类中断种调用了系统接口(如图2所示),而二类中断处理返回后进入一类中断的处理。随后进入被抢占的一类中断的处理过程,在其处理完成后,必须避免无法唤醒OS进行重调度的情况;否则,必须等到下一次系统重调度的时机,这无疑是实时系统无法接受的。
图2 中断嵌套示意图
有一个简单易行的经验法则,即如果一类中断的优先级不低于二类中断,可以在解决上述问题的同时,最大限度地保证软件的可移植性。显然,这一方法直接阻断了二类中断抢占一类中断的可能性,但灵活性欠佳,对软件设计造成了额外的约束。
在ARM V7的硬件环境下,通常采用可悬起中断(PendSV)来解决这一问题,即将PendSV的优先级设置为最低,并在其ISR中进行任务重调度后的上下文切换。由于PendSV可pending,即先悬起,可以先挂个号,等待其他中断处理完成后再执行。显然,这是一个更好的解决方案。