在当前的任务中,并没有关于就绪表等全局变量的访问,当前的任务也有自己的堆栈空间,我并不知道是否有更高优先级的任务就绪,之所以产生这些疑惑是没有搞清楚什么时候发生调度,怎么知道需要调度。当前运行的任务,一般而言就是所谓的最高优先级的任务,在没有访问一系列全局变量的过程中,内核又是如何知道存在一个更高优先级的任务被就绪了呢?
一般而言,对于抢占型实时内核,一般在同步、或者通信的过程中会主动的调用调度函数,或者任务的挂起函数中使用调度函数,其他的函数中并没有发现其他的调度函数,而且这种情况下都是手动的调度任务,那么在没有这些函数的情况下,实时操作系统中内核是如何知道需要调度的呢?
我仔细查找了一些资料,别人总结了一些操作系统发生调度的原因如下:
(1)正在执行的进程执行完毕。这时,如果不选择新的就绪进程执行,将浪费处理机资源。
(2)执行中进程自己调用阻塞原语将白己阻塞起来进入睡眠等状态。
(3)执行中进程调用了P原语操作,从而因资源不足而被阻塞;或调用了v原语操作激活了等待资源的进程队列。
(4)执行中进程提出I/O请求后被阻塞。
(5)在分时系统中时间片已经用完。
(6)在执行完系统调用等系统程序后返回用户进程时,这时可看作系统进程执行完毕,从而可调度选择一新的用户进程执行。
以上都是在可剥夺方式下的引起进程调度的原因。在CPU执行方式是可剥夺时.还有
(7)就绪队列中的某进程的优先级变得高于当前执行进程的优先级,从而也将引发进程调度。
我对比了在实时操作系统中经常使用的调度方式发现,原因(2)、(3)、(7)是主要的原因,其他的一般在实时操作系统中很难找到。但是这还是不能回答什么时候发生调度这个问题。
我认为在实时操作系统中发生调度的主要有两个部分:
(1)自身需要睡眠等待,必须手动的调用调度函数(信息量,或者通信机制)。
(2)发生中断过,当执行完中断服务函数以后,需要重新调度。
其中原因(2)是我们在分析实时操作系统中实时性能的主要因素,很多人又会有很多的疑问,如果操作系统中很少使用中断,实质上在实时系统中必须存在的一个中断就是时间节拍中断,这个中断的存在就能保证实时操作系统的实时型。这个时间节拍选择也是设计过程中必须注意的。我们可以参看uC/OS-II的时间节拍代码,其中完成了所有对非任务挂起的任务的就绪操作(时间到期),这时也就知道了那个任务需要我们调度。在其他的中断服务函数执行完成以后也就需要那个任务需要被执行,进而实现了实时操作。
void OSTimeTick (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
OSTimeTickHook(); /* Call user definable hook */
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
#endif
if (OSRunning == TRUE) {
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make task R-to-R (timed out)*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else {
/* Yes, Leave 1 tick to prevent ... */
ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */
} /* ... suspension is removed. */
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}
从上面的代码中我们可以知道每个任务都会被扫描一次,检测是否能够就绪,如果能够就绪就将就绪表中的值设置,这样也就知道了是否有更高优先级的任务就绪,是否需要调度操作。因为时间节拍中断的不断发生就能保证最高优先级任务的发生。因此时间节拍中断函数是在实时操作系统中非常重要的函数之一。当然任务之间切换以及在中断中切换到新的任务中的切换代码也是非常重要的,但是这些一般涉及到CPU寄存器的值,需要汇编代码实现。
因为中断完成以后很多的任务可能因为信号量等信息的释放已经就绪,这时候必然需要任务的调度操作,这时候也就知道了那个任务是最高优先级的,那个任务应该被执行。这时也就是发生调度的时刻。
在UC/OS-II中通常采用关闭中断的方式进入临界区,因为关闭了中断,所有的中断服务函数都不会被执行,也就不会发生任务的调度。那么只有一个情况才会发生调度,也就是任务自身需要睡眠,手动选择调度函数,但是在临界区中不应该发生睡眠等,因此也就不可能手动调度,因此所有发生调度的可能都被清除了,这样也就保证了临界区代码的安全性。