reactos操作系统实现(44)

前面分析了怎么样把一个线程放到延迟就绪队列,接着下来的问题就是这些在就绪队列里的线程什么时候开始运行呢?又是怎么样把就绪队列的线程取出来运行的呢?线程调度的触发事件有以下四种:

1) 线程进入就绪状态,如一个刚创建的线程,或者一个刚结束的线程。

2) 线程的时间片用完。

3) 线程调用系统服务发生等待,或者被系统改变其优先级。

4) 线程改变自己运行的处理器。

 

先来分析第一种情况,当线程结束时产生的调度。可以从ReactosAPI里知道,终止一个线程可以使用API函数TerminateThread,而这个函数就是通过系统调用转换后,调用内核的函数NtTerminateThread,而NtTerminateThread又调用线程结束函数PspTerminateThreadByPointer,紧接着它又调用函数PspExitThread,在这个函数调用内核线程结束函数KeTerminateThread。在内核函数里调用函数KiSwapThread来进行线程调度切换。

 

第二种情况,就是线程的时间片用完。当每次时间中断后,就会调用时钟处理函数HalpClockInterrupt,最后依次调用下面的函数:

KeUpdateSystemTime 更新系统时钟函数。

HalRequestSoftwareInterrupt 请求软件中断函数。

HalEndSystemInterrupt 处理软件中断结束。

SoftIntHandlerTable2 通过中断表调用KiDispatchInterrupt函数。

KiSwapContextInternal  进行线程的上下文切换,也就是切换线程。

 

第三种情况,当系统发生等待时,比如调用内核函数KeWaitForSingleObject等待时,就会调用函数KiSwapThread来进行线程切换。

 

可以看到好几个地方都需要调用函数KiSwapThread来切换线程,其实它就是把延迟就绪队列里的线程选择合适的线程来运行。它的代码如下:

#001  NTSTATUS

#002  FASTCALL

#003  KiSwapThread(IN PKTHREAD CurrentThread,

#004               IN PKPRCB Prcb)

#005  {

#006      BOOLEAN ApcState = FALSE;

#007      KIRQL WaitIrql;

#008      LONG_PTR WaitStatus;

#009      PKTHREAD NextThread;

#010      ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);

#011  

 

获取PRCB的锁。

#012      /* Acquire the PRCB lock */

#013      KiAcquirePrcbLock(Prcb);

#014 

 

获取当前处理器的下一个运行的线程。

#015      /* Get the next thread */

#016      NextThread = Prcb->NextThread;

#017      if (NextThread)

#018      {

 

如果处理器对象里已经有一个准备好运行的线程,就立即运行它,设置线程的运行状态为运行。

#019          /* Already got a thread, set it up */

#020          Prcb->NextThread = NULL;

#021          Prcb->CurrentThread = NextThread;

#022          NextThread->State = Running;

#023      }

#024      else

#025      {

 

当前处理器对象里没有就绪线程,就从延迟队列里查找到合适的线程来运行。

#026          /* Try to find a ready thread */

#027          NextThread = KiSelectReadyThread(0, Prcb);

#028          if (NextThread)

#029          {

 

找到了可以运行的线程,设置这个线程为运行状态。

#030              /* Switch to it */

#031              Prcb->CurrentThread = NextThread;

#032              NextThread->State = Running;

#033          }

#034          else

#035          {

 

如果线程延迟就绪队列里没有可以运行的线程,就设置当前CPU为空闲状态。

#036              /* Set the idle summary */

#037              InterlockedOr((PLONG)&KiIdleSummary, Prcb->SetMember);

#038 

 

选择处理器缺省的空闲线程来运行。

#039              /* Schedule the idle thread */

#040              NextThread = Prcb->IdleThread;

#041              Prcb->CurrentThread = NextThread;

#042              NextThread->State = Running;

#043          }

#044      }

#045 

 

释放处理器锁。

#046      /* Sanity check and release the PRCB */

#047      ASSERT(CurrentThread != Prcb->IdleThread);

#048      KiReleasePrcbLock(Prcb);

#049 

 

保存当前的IRQL

#050      /* Save the wait IRQL */

#051      WaitIrql = CurrentThread->WaitIrql;

#052 

 

更新下一个运行线程的内存空间。

#053      /* REACTOS Mm Hack of Doom */

#054      MiSyncForContextSwitch(NextThread);

#055 

 

调用函数KiSwapContext来切换线程的运行环境。

#056      /* Swap contexts */

#057      ApcState = KiSwapContext(CurrentThread, NextThread);

#058 

#059      /* Get the wait status */

#060      WaitStatus = CurrentThread->WaitStatus;

#061 

 

检查是否需要进行异步调用。

#062      /* Check if we need to deliver APCs */

#063      if (ApcState)

#064      {

#065          /* Lower to APC_LEVEL */

#066          KeLowerIrql(APC_LEVEL);

#067 

#068          /* Deliver APCs */

#069          KiDeliverApc(KernelMode, NULL, NULL);

#070          ASSERT(WaitIrql == 0);

#071      }

#072 

 

设置为低的优先级。

#073      /* Lower IRQL back to what it was and return the wait status */

#074      KeLowerIrql(WaitIrql);

#075      return WaitStatus;

#076  }

 

你可能感兴趣的:(reactos操作系统实现(44))