背景
大多数嵌入式RTOS在Cortex-M3/M4上的移植都需要PendSV,比如uCOS、RT-Thread、FreeRTOS等,本文就对PendSV的功能作用,以及为什么需要PendSV进行详细的分析。
PendSV是什么?
我们先引用《Cortex-M3权威指南》对PendSV的介绍:
PendSV(可悬起的系统调用),它是一种CPU系统级别的异常,它可以像普通外设中断一样被悬起,而不会像SVC服务那样,因为没有及时响应处理,而触发Fault。
个人理解PendSV的英文全称应该是:Pend System Service Call,简称PendSV. 所以PendSV的最大特点就是,它是系统级别的异常,但它又天生支持【缓期执行】。
PendSV使用场景
由于PendSV的特点就是支持【缓期执行】,所以嵌入式OS可以利用它这个特点,进行任务调度过程的上下文切换。而为什么要使用【缓期执行】的特点来进行上下文切换呢?简单的说就是任何嵌入式RTOS,都需要尽量不打断外设中断。 我们来举例说明,假如一个系统中有2个就绪的任务,上下文切换被切换的场合有:
- 执行了一个系统调用SVC。
- 系统滴答定时器SYSTICK中断,触发了任务的调度。
没有外部中断,OS直接切换任务
假如我们在Systick中断服务程序中,启动上下文切换,流程图如下:
上图中,似乎一切都正常,任务调度很流畅,但现实总是很骨感的,不可能这么简单,假如在产生异常时,CPU正在响应另一个中断ISR1,而SysTick的优先级又大于ISR1,在这种情况下,SysTick就会抢占ISR1,获取CPU使用权,但是在SysTick中不能进行上下文切换,因为这将导致中断ISR1被延迟,这在实时要求的系统中是不能容忍的,但是这个说辞只是为了方便理解,更重要的是:
在Cortex-M3中,如果OS在某个中断活跃时,抢占了该中断,而且又发生了任务调度,并执行了任务,切换到了线程运行模式,将直接触发Fault异常。
SysTick优先级高于外部中断,OS抢占IRQ进行任务调度触发Fault
如下图所示: 为了解决这个问题,早起的OS大多会检测当前是否有中断在活跃中,只有在无任何中断需要响应时,才进行上下文切换,切换期间无法响应中断。 这个时候,可能会有人想,既然有上面的原因限制,我能不能将SysTick的优先级设置为最低,然后在SysTick中进行上下文切换,然后任务调度呢? 答案是:可以。这一点在Cortex-M3权威指南中没有解释,反而影响了很多读者对于PendSV的理解。按照上面的思路,我们分析一下整体流程:
Systick中断优先级低与IRQ,任务调度流程
在上图中我们可以看到,当OS的Systick中断级别低于外部中断时,确实不会触发Fault,但是这带来了一个问题:
一般OS在调度任务时,会关闭中断,也就是进入临界区,而OS任务调度是要耗时的,这就会出现一种情况: 在任务调度期间,如果新的外部IRQ发生,CPU将不能够快速响应处理。
将SysTick的优先级调低,避免了触发Fault的问题,但是会影响外部中断IRQ的处理速度,那有没有进一步优化的方法呢?答案就是PenSV。因为PendSV有【缓期执行】的特点,所以可以将上图中的OS拆分,分成2段:
- 滴答定时器中断,制作业务调度前的判断工作,不做任务切换。
- 触发PendSV,PendSV并不会立即执行,因为PendSV的优先级最低,如果此时正好有IRQ请求,那么先响应IRQ,最后等到所有优先级高于PendSV的IRQ都执行完毕,再执行PendSV,进行任务调度。
Systick、PendSV优先级低,只在PendSV中进行上线文切换,任务调度
上述的流程就是目前常见的嵌入式RTOS的任务调度流程,uC/OS和FreeRTOS都会将Systick和PendSV的优先级设置为最低。 到这里,可能会有人又有疑问,这样做是不是也有缺点:
- SysTick的优先级最低,那如果外部IRQ比较频繁,是不是会导致SysTick经常被挂起,然后滞后,导致Systick的节拍延长,进而导致不准啊?
- 因为1的原因,导致任务的执行调度就不够快了?
答案:确实存在这些问题,但是这些问题影响面已经很小了。我们能不能将SysTick的优先级设置成最高,将PendSV的优先级设置为低,就能完美的解决上述问题,我们不妨分析下这种情况: 这样似乎解决了问题,但是又带来了一个问题,因为SysTick的优先级最高,而且又是周期性的触发,会导致经常抢占外部IRQ,这就会导致外部IRQ响应变慢,这在一些对实时性要求高的,比如按键、断电中断等待,是不能接受的,你肯定不希望你的按键扫描体验卡顿。 所以,没有十全十美的解决方案,关键是要看我们更关注什么?对于CPU来说,嵌入式OS也是一个程序,跟普通的裸机程序是一样的,无非就是复杂一些,涉及到了手动切换堆栈、PC等高级操作而已,OS的优先级天生就没有外部中断的优先级高。
小结
- PendSV是可悬起系统中断,具有【缓期执行】特征。
- PendSV一般被嵌入式OS用于 任务调度的上下文切换。
- 使用PendSV时,一般会将PendSV的优先级设置为最低,让外部IRQ先执行,等到没有外部中断后,在执行上下文切换。
- 嵌入式实时操作系统的【实时】概念,并不仅仅指应用程序、任务的调度实时,而是指整个系统的实时性高,具备【可剥夺】特点,当有高优先级的中断、任务具备执行条件时,立刻中断当前正在运行的任务,去执行高优先级的IRQ和高优先级任务。
- 嵌入式OS一般会将SysTick的优先级也设置为最低,保证外部中断IRQ优先,详细的分析,我们下一篇文章讨论。
|