UCOSII学习笔记【三】之任务调度
前一节中用到的是书中给的第一个例子,由例子入手很容易从整体上把握整个操作系统的运行,不会像操作系统相关书中讲的那么抽象。在这之前我也对一些基本的C语言的知识进行了相应的复习和总结。其实操作系统中用到的最多的基础知识是对一些数据结构知识的理解,这个以后有机会再进行总结。
对于ucosII,总是基于最高优先级的,即总是执行最高优先级的任务。那么确定哪个任务的优先级最高,就该这个任务执行了。这一工作是怎么实现的呢?对于任务的调度在ucosII中有两个函数实现这一功能。void OS_Sched(void) 和 void OSIntEx他()函数。我们先看OS_Sched()函数,代码如下:
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0) { /* ... scheduler is not locked */
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
我们可以看到其实该函数实现的过程很简单。而在函数OS_TASK_SW()(其实是一个宏调用)执行之前的代码部分实现的功能主要是对任务调度进行一些判断。我们暂时可以不管。只要满足了上面的一系列的if语句后,就可以执行任务的切换,即执行函数OS_TASK_SW();该任务切换很简单,主要由两步完成:(1)将被挂起的任务的处理器寄存器推入堆栈,然后将较高优先级任务的寄存器值从堆栈张恢复到寄存器中。此操作就像中断中的保存现场和恢复现场的操作一样,相信大家一定会很熟悉。而OS_TASK_SW()宏调用是跟具体的实现平台有关,需要直接操纵寄存器,故不能由C语言实现。我们可以在此粗略的看到任务调度的算法实现其实是很简单的。当然在执行调度之前我们是要找到优先级最高的任务的。
再看看另外一个任务调度的程序OSIntExt()函数,该函数是在离开中断服务子程序时进行调用的,在系统进入中断后,会使当前的任务进入中断态,此时在中断退出时要再次进行相应的判断,查找到此时的优先级最高的任务,如果此时的最高优先级等于被中断的任务的优先级,就会再次返回到被中断的任务中继续执行,但是如果此时有更高优先级的任务因为中断进入了就绪态,就会执行更高优先级的任务,而此时被中断的任务就进入了中断服务态:
void OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (OSRunning == OS_TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
if (OSIntNesting == 0) { /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0) { /* ... and not locked. */
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Keep track of the number of ctx switches */
OSIntCtxSw(); /* Perform interrupt level ctx switch */
}
}
}
OS_EXIT_CRITICAL();
}
}
一般来讲,OSIntExt是跟OSIntEnter配套使用的。顾名思义,大家都能明白两个函数名所要表达的意思。一个是进入Int(中断)一个是退出Int。
此处的OSIntCtxSw()函数实现的功能基本上和OS_Sched相同,但是有一点不同的是在中断服务子程序中已经将CPU寄存器存入到了中断了的任务的堆栈中,而无需再调用OS_Task_SW做第二次保存。