reactos操作系统实现(44)
前面分析了怎么样把一个线程放到延迟就绪队列,接着下来的问题就是这些在就绪队列里的线程什么时候开始运行呢?又是怎么样把就绪队列的线程取出来运行的呢?线程调度的触发事件有以下四种: 1) 线程进入就绪状态,如一个刚创建的线程,或者一个刚结束的线程。 2) 线程的时间片用完。 3) 线程调用系统服务发生等待,或者被系统改变其优先级。 4) 线程改变自己运行的处理器。
先来分析第一种情况,当线程结束时产生的调度。可以从Reactos的API里知道,终止一个线程可以使用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} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |