μC/OS-II的内核结构
上述两步完成以后,用户可以开始服务于叫中断的设备了[L3.15(3)]。这一段完全取决于应用。μC/OS-Ⅱ允许中断嵌套,因为μC/OS-Ⅱ跟踪嵌套层数OSIntNesting。然而,为允许中断嵌套,在多数情况下,用户应在开中断之前先清中断源。
调用脱离中断函数OSIntExit()[L3.15(4)]标志着中断服务子程序的终结,OSIntExit()将中断嵌套层数计数器减1。当嵌套计数器减到零时,所有中断,包括嵌套的中断就都完成了,此时μC/OS-Ⅱ要判定有没有优先级较高的任务被中断服务子程序(或任一嵌套的中断)唤醒了。如果有优先级高的任务进入了就绪态,μC/OS-Ⅱ就返回到那个高优先级的任务,OSIntExit()返回到调用点[L3.15(5)]。保存的寄存器的值是在这时恢复的,然后是执行中断返回指令[L3.16(6)]。注意,如果调度被禁止了(OSIntNesting>0),μC/OS-Ⅱ将被返回到被中断了的任务。
以上描述的详细解释如图F3.5所示。中断来到了[F3.5(1)]但还不能被被CPU识别,也许是因为中断被μC/OS-Ⅱ或用户应用程序关了,或者是因为CPU还没执行完当前指令。一旦CPU响应了这个中断[F3.5(2)],CPU的中断向量(至少大多数微处理器是如此)跳转到中断服务子程序[F3.5(3)]。如上所述,中断服务子程序保存CPU寄存器(也叫做CPUcontext)[F3.5(4)],一旦做完,用户中断服务子程序通知μC/OS-Ⅱ进入中断服务子程序了,办法是调用OSIntEnter()或者给SIntNesting直接加1[F3.5(5)]。然后用户中断服务代码开始执行[F3.5(6)]。用户中断服务中做的事要尽可能地少,要把大部分工作留给任务去做。中断服务子程序通知某任务去做事的手段是调用以下函数之一:OSMboxPost(),OSQPost(),OSQPostFront(),OSSemPost()。中断发生并由上述函数发出消息时,接收消息的任务可能是,也可能不是挂起在邮箱、队列或信号量上的任务。用户中断服务完成以后,要调用OSIntExit()[F3.5(7)]。从时序图上可以看出,对被中断了的任务说来,如果没有高优先级的任务被中断服务子程序激活而进入就绪态,OSIntExit()只占用很短的运行
时间。进而,在这种情况下,CPU寄存器只是简单地恢复[F3.5(8)]并执行中断返回指令
[F3.5(9)]。如果中断服务子程序使一个高优先级的任务进入了就绪态,则OSIntExit()将占用
较长的运行时间,因为这时要做任务切换[F3.5(10)]。新任务的寄存器内容要恢复并执行中
断返回指令[F3.5(12)]。

图 3.5 中断服务
进入中断函数 OSIntEnter()的代码如程序清单 L3.16 所示,从中断服务中退出函数
OSIntExit()的代码如程序清单 L3.17 所示。如前所述,OSIntEnter()所做的事是非常少的。
程序清单 L3.16 通知μC/OS-Ⅱ,中断服务子程序开始了.
voidOSIntEnter(void)
{
OS_ENTER_CRITICAL();
OSIntNesting++;
OS_EXIT_CRITICAL();
}
恢复所有CPU寄存器; (5)
程序清单 L3.17 通知μC/OS-Ⅱ,脱离了中断服务
voidOSIntExit(void)
{
OS_ENTER_CRITICAL();(1)
if((--OSIntNesting|OSLockNesting)==0){(2)
OSIntExitY=OSUnMapTbl[OSRdyGrp];(3)
OSPrioHighRdy=(INT8U)((OSIntExitY3)+
OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
if(OSPrioHighRdy!=OSPrioCur){
OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++;
OSIntCtxSw();(4)
}
}
OS_EXIT_CRITICAL();
}
OSIntExit()看起来非常像OSSched()。但有三点不同。第一点,OSIntExit()使中断
嵌套层数减1[L3.17(2)]而调度函数OSSched()的调度条件是:中断嵌套层数计数器和锁定
嵌套计数器(OSLockNesting)二者都必须是零。第二个不同点是,OSRdyTbl[]所需的检索
值Y是保存在全程变量OSIntExitY中的[L3.17(3)]。这是为了避免在任务栈中安排局部变
量。这个变量在哪儿和中断任务切换函数OSIntCtxSw()有关,(见9.04.03节,中断任务
切换函数)。最后一点,如果需要做任务切换,OSIntExit()将调用OSIntCtxSw()[L3.17
(4)]而不是调用OS_TASK_SW(),正像在OSSched()函数中那样。
调用中断切换函数OSIntCtxSw()而不调用任务切换函数 OS_TASK_SW(),有两个原因,
首先是,如程序清单中L3.5(1)和图F3.6(1)所示,一半的工作,即CPU寄存器入栈的工作
已经做完了。第二个原因是,在中断服务子程序中调用OSIntExit()时,将返回地址推入
了堆栈[L3.15(4)和F3.6(2)]。OSIntExit()中的进入临界段函数OS_ENTER_CRITICAL()或
许将CPU的状态字也推入了堆栈L3.7(1)和F3.6(3)。这取决于中断是怎么被关掉的(见第8
章移植μC/OS-Ⅱ)。最后,调用OSIntCtxSw()时的返回地址又被推入了堆栈[L3.17(4)和
F3.1(4)],除了栈中不相关的部分,当任务挂起时,栈结构应该与μC/OS-Ⅱ所规定的完全
一致。OSIntCtxSw()只需要对栈指针做简单的调整,如图F3.6(5)所示。换句话说,调整
栈结构要保证所有挂起任务的栈结构看起来是一样的。

图3.6中断中的任务切换函数OSIntCtxSw()调整栈结构
有的微处理器,像Motorola68HC11中断发生时CPU寄存器是自动入栈的,且要想允许
中断嵌套的话,在中断服务子程序中要重新开中断,这可以视作一个优点。确实,如果用
户中断服务子程序执行得非常快,用户不需要通知任务自身进入了中断服务,只要不在中
断服务期间开中断,也不需要调用OSIntEnter()或OSIntNesting加1。程序清单L3。18
中的示意代码表示这种情况。一个任务和这个中断服务子程序通讯的唯一方法是通过全程
变量。
评论