嵌入式操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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,然后把当前线程插入共享对象的等待队列。但若在插入等待队列前发生中断,则被中断的核心线程(当前核心线程)就会处于这种状态。

对于这种状态的核心线程,也是采取放行的策略。