网站使用问题,网络营销项目策划,wordpress 显示异常,一个网站如何做cdn加速前言 Windows 线程调度器的实现分散在内核各处#xff0c;并且与许多组件都有关联#xff0c;很难进行系统地学习#xff0c;所以我打算写几篇文章来记录下自己学习过程中的思考和分析#xff0c;同时也方便日后查阅#xff0c;此文可以看作是《Windows内核原理与实现》中… 前言 Windows 线程调度器的实现分散在内核各处并且与许多组件都有关联很难进行系统地学习所以我打算写几篇文章来记录下自己学习过程中的思考和分析同时也方便日后查阅此文可以看作是《Windows内核原理与实现》中线程调度部分的读书笔记和简单总结。 正文 一. 线程当前状态 在对调度器函数进行分析学习之前首先要明确一个概念调度器只由内核层进行负责实现不涉及执行体层。因此线程相关的数据结构只有 KTHREAD其中调度相关的最重要的成员是 State它标识了线程的当前状态取值由名为 KTHREAD_STATE 的枚举类型定义 typedef enum _KTHREAD_STATE {Initialized,Ready,Running,Standby,Terminated,Waiting,Transition,DeferredReady,GateWait
} KTHREAD_STATE; 已初始化 (Initialized)线程创建过程中的内部状态此时线程不参与调度。就绪 (Ready)线程已经准备好运行等待被调度。运行中 (Running)线程正在某一处理器上运行。待命 (Standby)线程被选为某一处理器上下一个将要被执行的线程。已终止 (Terminated)线程已终止正在进行资源回收。等待中 (Waiting)线程正在等待某个条件满足比如事件对象被触发。转移 (Transition)线程已经准备好运行但内核栈不在内存中。延迟就绪 (DeferredReady)线程尚未被确定在哪个处理器上运行此状态对于单处理器系统没有意义。门等待 (GateWait)线程正在等待一个门对象。 其中就绪和延迟就绪状态的主要区别是延迟就绪线程尚未确定被分配到哪个处理器上运行而就绪线程已经被分配到了某个处理器上。 对于线程各个状态间的转移规则可以参考线程状态转移图引自潘爱民老师的《Windows内核原理与实现》 二. 进程的当前状态 进程在内核层所对应的 KPROCESS 结构中也有一个用来标识当前状态的 State 成员它的取值由名为 KPROCESS_STATE 的枚举类型定义 typedef enum _KPROCESS_STATE {ProcessInMemory,ProcessOutOfMemory,ProcessInTransition,ProcessOutTransition,ProcessInSwap,ProcessOutSwap
} KPROCESS_STATE; ProcessInMemory表示进程的虚拟地址空间内容在物理内存中。ProcessOutOfMemory表示进程的虚拟地址空间内容已被换出物理内存。ProcessInTransition表示进程的虚拟地址空间内容不在物理内存中但已请求换入。ProcessOutTransition表示进程的虚拟地址空间内容存在于物理内存中但已请求换出。ProcessInSwap表示正在将进程的虚拟地址空间内容换入物理内存换入完成后状态将变更为 ProcessInMemory。ProcessOutSwap表示正在将进程的虚拟地址空间内容换出物理内存换出完成后状态将变更为 ProcessOutOfMemory。 换入或换出进程的虚拟地址空间会导致进程状态的切换此工作是由名为 平衡集管理器 (Balance Set Manager) 的内核组件负责的在内核第一阶段初始化接近结束时MmInitSystem 函数创建了两个平衡集管理器线程其对应例程分别是 KeBalanceSetManager 和 KeSwapProcessOrStack 函数。 KeBalanceSetManager 线程循环等待一个每秒触发一次的定时器对象和一个工作集管理器事件对象当等待成功后它触发名为 KiSwapEvent 的事件对象来通知交换线程以尝试对满足条件的线程的内核栈执行换出操作。KeSwapProcessOrStack 即为交换线程它循环等待上述的 KiSwapEvent 对象一旦等待成功会根据情况执行进程和线程内核栈的换入换出工作。 一个进程的换出操作发生在进程的 StackCount 为 0 时StackCount 记录了该进程中有多少个线程的内核栈位于内存中当该进程的所有线程的内核栈都被换出内存时KiOutSwapKernelStacks 会将进程插入到待换出链表中并触发 KiSwapEvent 对象交换线程会在下次循环中调用 KiOutSwapProcesses 函数将该进程换出内存。 平衡集管理器实质上是内存管理器组件有关它更多更详细的内容将在之后的文章中更新。 三. 调度器主要函数实现 1. KiReadyThread KiReadyThread 从名字上来看是将一个线程转为就绪状态而实际上这个函数根据三种不同情况来进行处理 void __fastcall KiReadyThread(IN PKTHREAD Thread) {PKPROCESS Process;Process Thread-ApcState.Process;if (Process-State ! ProcessInMemory) {Thread-State Ready;Thread-ProcessReadyQueue TRUE;InsertTailList(Process-ReadyListHead, Thread-WaitListEntry);if (Process-State ProcessOutOfMemory) {Process-State ProcessInTransition;InterlockedPushEntrySingleList(KiProcessInSwapListHead, Process-SwapListEntry);KiSetInternalEvent(KiSwapEvent, KiSwappingThread);}return;} else if (Thread-KernelStackResident FALSE) {ASSERT(Process-StackCount ! MAXULONG_PTR);Process-StackCount 1;ASSERT(Thread-State ! Transition);Thread-State Transition;InterlockedPushEntrySingleList(KiStackInSwapListHead, Thread-SwapListEntry);KiSetInternalEvent(KiSwapEvent, KiSwappingThread);return;} else {KiInsertDeferredReadyList(Thread);return;}
} 分支一 首先此函数根据上文提到的 KPROCESS 的 State 成员来判断目标线程所属进程当前是否处于 ProcessInMemory 状态即进程虚拟地址空间是否在物理内存中若不是则将目标线程设置为就绪状态并将线程的 ProcessReadyQueue 标志设置为 TRUE然后将线程插入到所属进程的就绪链表 (ReadyListHead) 中ProcessReadyQueue 用来标识线程是否在其所属进程的就绪链表中。而后进一步判断进程是否处于 ProcessOutOfMemory 状态若是则将该进程设置为 ProcessInTransition 状态并插入到待换入进程链表中最后触发 KiSwapEvent 对象通知交换线程执行进程换入操作。由此可以看出ProcessInTransition 是一种中间状态他标识了进程将要但还没有被执行换入操作此状态介于 ProcessInMemory 和 ProcessInSwap 之间。 当进程当前处于 ProcessOutOfMemory 状态时其后续操作是平衡集管理器的交换线程成功等待到 KiSwapEvent进而调用 KiInSwapProcesses 函数将之前插入到待换入进程链表中的进程换入内存通过 MmInSwapProcess 函数之后将进程状态修改为 ProcessInMemory。此时进程虚拟地址空间已在物理内存中可以对进程中所有的就绪线程进行调度所以 KiInSwapProcesses 函数遍历该进程的就绪链表对其中的所有线程再次调用 KiReadyThread而后将线程从链表中移除。由于这一次进程已存在于内存中所以此次 KiReadyThread 函数不会再执行到此分支。 而对于 ProcessInTransition 和 ProcessInSwap 这两种状态则不需要通知交换线程将进程换入内存因为此时交换线程已经或将要执行 KiInSwapProcesses 函数如上所述此函数会在将进程换入内存后对该进程就绪链表中的所有线程再次调用 KiReadyThread。 最后若进程处于 ProcessOutTransition 或 ProcessOutSwap 状态进程因其所有线程的内核栈都被换出内存而导致自身也被换出内存在换出的过程中如果有属于该进程的新线程被创建或某一现有线程挂靠到该进程上则 KiReadyThread 被调用此时进程可能处于这两种状态那么剩下的工作将由交换线程通过调用 KiOutSwapProcesses 函数来完成此函数负责将待换出进程链表中的进程换出内存它在两个阶段分别检查待换出进程的就绪链表若进程尚未换出内存则取消换出操作并将进程状态修改为 ProcessInMemory然后对该进程就绪链表中的所有线程再次调用 KiReadyThread若进程已换出内存则修改进程状态为 ProcessInTransition 并触发 KiSwapEvent 对象交换线程会在下次循环中调用 KiInSwapProcesses 执行后续操作。 综上所述只有当进程处于 ProcessOutOfMemory 状态时此函数才通知交换线程将进程换入内存其余情况平衡集管理器会进行判断和处理而无论哪种一情况进程最后都会变为 ProcessInMemory 状态进而交由其他分支处理所谓异途同归。 分支二 如果进程当前处于 ProcessInMemory 状态经分支一处理后进程必然处于此状态则继续判断目标线程的内核栈是否在物理内存中由 KernelStackResident 标志指示。上文提到线程栈的换入和换出操作也是由平衡集管理器负责的当一个线程处于等待状态超过一定时间之后交换线程调用 KiOutSwapKernelStacks 函数将其内核栈换出物理内存。因此若线程的内核栈已被换出物理内存则要先通知交换线程将内核栈其换入内存交换线程通过调用 KiInSwapKernelStacks 函数将线程内核栈换入物理内存而后直接调用 KiInsertDeferredReadyList 函数将线程插入到延迟就绪链表中关于 KiInsertDeferredReadyList 函数见分支三。 另外上文还提到进程 KPROCESS 对象中的 StackCount 成员记录了该进程中有多少个线程的内核栈位于内存中对于一个将要被换入内存的线程自然要将其所属进程的 StackCount 加一由于线程终止或挂靠到其他进程时也会引起 StackCount 的变动所以此成员不由平衡集管理器维护。 分支三 进入到分支三就表示线程已满足执行条件内核栈和所属进程都已在物理内存中因此调用 KiInsertDeferredReadyList 函数执行下一步操作 PKPRCB Prcb;
Prcb KeGetCurrentPrcb();
Thread-State DeferredReady;
Thread-DeferredProcessor Prcb-Number;
PushEntryList(Prcb-DeferredReadyListHead, Thread-SwapListEntry); 此函数逻辑十分简单所做的仅仅是将线程设置为延迟就绪状态并将其插入到当前处理器 PRCB 结构中的延迟就绪链表中以后当调度器获得控制权时KiProcessDeferredReadyList 函数将遍历此链表并对每个线程调用 KiDeferredReadyThread 函数使其有机会变为就绪或待命状态。注此处所说的就绪状态是真正的就绪区别于上文所说进程就绪链表中的线程后者不满足执行条件需要等待其所属进程被换入内存。 至此 KiReadyThread 函数已分析完毕可以看出经过此函数处理后的任何线程都会变为延迟就绪状态这对线程来说是一个重要转折点意味着它将有机会获得执行权而在此之前该线程不会被考虑执行。 TO BE CONTINUED ...