4.5.2 线程的调度——中断上下文
在Hello China V1.0的实现中,只会在系统时钟中断发生的时候,才会对线程进行重新调度。这样对普通的应用可能可以满足,但对于一些实时的应用,则可能会有问题。因此,在Hello China V1.5的实现中,对线程的调度,不再局限于时钟中断中,而安排在所有中断中,在任何中断(包括系统时钟中断)处理程序执行完毕,返回用户线程之前,都会做一个线程调度。
在V1.0的实现中,对于所有线程调度的功能,都是在ScheduleFromInt函数中实现的,该函数原型如下:
static VOID ScheduleFromProc(__COMMON_OBJECT* lpThis,LPVOID lpEsp);
在V1.5的实现中,仍然保留了该函数,但对其进行了更改,下面是在V1.5中ScheduleFromProc的实现代码:
static VOID ScheduleFromProc(__COMMON_OBJECT* lpThis,LPVOID lpEsp) { if((NULL==lpThis) || (NULL==lpEsp)) //Invalid parameters. { return; } __KERNEL_THREAD_MANAGER* lpMgr=(__KERNEL_THREAD_MANAGER*) lpThis; __KERNEL_THREAD_OBJECT* lpThread=NULL; __KERNEL_THREAD_OBJECT* lpCurrentThread=NULL; lpCurrentThread=lpMgr->lpCurrentKernelThread; if(NULL==lpCurrentThread) //Called first time. { lpThread=lpMgr->GetScheduleKernelThread((__COMMON_OBJECT*) lpMgr, 0); //Get one kernel thread from ready queue to schedule. if(NULL==lpThread) //Not any kernel thread exists,fatal error! { PrintLine("Fatal error!No kernel thread has been created!"); return; } lpMgr->lpCurrentKernelThread=lpThread; lpThread->dwThreadStatus=KERNEL_THREAD_STATUS_RUNNING; lpThread->dwTotalRunTime+=SYSTEM_TIME_SLICE; __SwitchTo(lpThread->lpKernelThreadContext); //Switch to new thread. return; //Should not reach here. } else { lpCurrentThread->lpKernelThreadContext= (__KERNEL_THREAD_CONTEXT*)lpEsp; //Save context. switch(lpCurrentThread->dwThreadStatus) { //Allow the thread with following status to continue to run. case KERNEL_THREAD_STATUS_READY: case KERNEL_THREAD_STATUS_SUSPENDED: case KERNEL_THREAD_STATUS_SLEEPING: case KERNEL_THREAD_STATUS_TERMINAL: case KERNEL_THREAD_STATUS_BLOCKED: { lpCurrentThread->dwTotalRunTime+= SYSTEM_TIME_SLICE; //Update time slice. __SwitchTo((__KERNEL_THREAD_CONTEXT*)lpEsp); return; //Should not reach here. } case KERNEL_THREAD_STATUS_RUNNING: //Should schedule. { lpThread=lpMgr->GetScheduleKernelThread( (__COMMON_OBJECT*)lpMgr, lpCurrentThread->dwThreadPriority); if(NULL==lpThread) //Current thread is most priority. { lpCurrentThread->dwTotalRunTime+=SYSTEM_TIME_SLICE; __SwitchTo((__KERNEL_THREAD_CONTEXT*)lpEsp); return; //Should not reach here. } else //Schedule the more priority thread. { lpCurrentThread->dwThreadStatus= KERNEL_THREAD_STATUS_READY; //Change status. lpMgr->AddReadyKernelThread((__COMMON_OBJECT*) lpMgr, lpCurrentThread); //Put to ready queue. lpThread->dwThreadStatus= KERNEL_THREAD_STATUS_RUNNING; lpThread->dwTotalRunTime+=SYSTEM_TIME_SLICE; lpMgr->lpCurrentKernelThread=lpThread; __SwitchTo(lpThread->lpKernelThreadContext); //Switch. return; //Should not reach here. } } default: { PrintLine("Fatal error: Invalid thread status encountered."); return; } } } }
对于处于临时状态的核心线程,V1.5的调度策略与V1.0类似,采用继续放行的策略。因为凡是处于这种状态的核心线程,必然是因为某种改变其运行状态的函数调用导致的,若这时候把处于这种状态的线程置换出去,则可能会引发系统数据不一致的情况。因此,继续让处于这种状态的线程执行,是一个相对简单的策略,因为这样的线程马上就会在过程上下文中,启动另外的调度。
核心线程处于下列任何一种状态,都被作为临时状态来处理。
● KERNEL_THREAD_STATUS_SUSPENDED
在核心线程被挂起的时候,会处于这种状态。之所以产生这种状态的核心线程,是当前核心线程在调用SuspendKernelThread函数的时候,把自己指定为待挂起线程。该函数首先设置当前运行核心线程的状态为KERNEL_THREAD_STATUS_SUSPENDED,并放入挂起队列,然后从就绪队列中选择另外一个优先级最高的核心线程投入运行。
若在当前核心线程的状态刚刚被设置为SUSPENDED,还没有放入挂起队列的时候,发生了中断,这样当前核心线程的状态就是KENREL_THREAD_STATUS_SUSPENDED。处于这种状态的核心线程,是一种临时状态,会在很短的时间内被切换出CPU。因此,若发生中断的时候,当前核心线程处于这种状态,则不作任何调度,而是恢复当前核心线程,继续让其执行(采取“放行”的策略)。因为在很短的时间内,又会发生一次线程调度。
● KERNEL_THREAD_STATUS_SLEEPING
核心线程在调用Sleep函数,但还未完全进入睡眠状态的时候,会处于正在运行,但状态为SLEEPING的情况。因为Sleep函数会首先把当前核心线程的状态设置为SLEEPING,然后插入睡眠队列,并从就绪队列中选择另外一个状态为KERNEL_THREAD_STATUS_READY的线程投入运行。
若核心线程的状态刚刚被设置为KERNEL_THREAD_STATUS_SLEEPING,还没有来得及被插入睡眠队列,这时候发生中断,则当前线程就是SLEEPING状态。对处于这种状态的核心线程,调度程序也不会打断,而是恢复其上下文,继续让其执行。因为在很短的时间内,该线程就会被切换出CPU。
● KERNEL_THREAD_STATUS_TERMINAL
在核心线程结束的时候,会处于KERNEL_THREAD_STATUS_TERMINAL。在核心线程结束运行的时候,首先会把自己的状态设置为KERNEL_THREAD_STATUS_TERMINAL,然后试图从就绪队列中选择另外一个状态为READY的线程投入运行。若这个过程中有中断发生,则在中断处理程序看来,当前核心线程会处于TERMINAL状态。对于这种状态的核心线程,也采取放行策略。
● KERNEL_THREAD_STATUS_BLOCKED
在核心线程等待一个核心对象的时候,会处于这种状态。核心线程调用WaitForThisObject或WaitForThisObjectEx函数,等待一个共享对象。在这些函数的处理中,会首先把当前核心线程的状态设置为KERNEL_THREAD_STATUS_BLOCKED,然后把当前线程插入共享对象的等待队列。但若在插入等待队列前发生中断,则被中断的核心线程(当前核心线程)就会处于这种状态。
对于这种状态的核心线程,也是采取放行的策略。