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}

相关文章

导入moment 使用方式 年月日,时分秒 星期几 相对时间 7天后 2小时后 明天 将毫秒转换成年月日
@ 一、前言 为什么介绍redux-actions呢? 第一次见到主要是接手公司原有的项目,发现有之前的大佬在处理...
十大React Hook库 原文地址:https://dev.to/bornfightcompany/top-10-react-hook-libraries-4065 原文...
React生命周期 React的生命周期从广义上分为挂载、渲染、卸载三个阶段,在React的整个生命周期中提供很...
React虚拟DOM的理解 Virtual DOM是一棵以JavaScript对象作为基础的树,每一个节点可以将其称为VNode,用...
React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ....