1. 介绍
2. NT调度程序工作方式的简单描述
3. 代码段抽取
3.1. 引擎信息
3.2. 用法
4. Phide2
4.1. 运行一个新的调度程序所必需的东西
4.2. 如何找到所需的非导出符号
4.2.1. NtYieldExecution
4.2.2. KiWait[In/Out]ListHead, KeDispatcherReadyListHead, KiReadySummary,
KiStackOutSwapRequest
4.2.3. KeBalanceSetManager, KeSwapProcessOrStack
4.2.4. KiStackInSwapListHead, KiProcess[In/Out]SwapListHead, KiSwapEvent
4.2.5. KiReadyQueueIndex
4.2.6. PsActiveProcessHead
4.3. 用法
5. 总结
6. 致谢
7. 相关链接
=============================================================================
1. 介绍
---------------
Joanna Rutkowska 编写的Klister 0.4 是一个很棒的程序,它通过遍历KiWaitInListHead,
KiWaitOutListHead 和 KiDispatcherReadyListHead数组列举存在的线程.它是为W2K专门设计的,
无法运用于其他的操作系统。Joanna 做过这样的假设,每个线程要么等待,要么就绪运行,它们都
属于这些链表中的一个。然而,我们都记得Joanna在Klister 0.3中的错误 - 她认为操作系统中的每
一个进程都必需有独一无二的进程标识(PID)。所以,那时她所说的“从刚才提到的内部调度链表中删除
隐藏进程中的线程是不太可能的”,在现在看来还对吗?因为我们的隐藏进程将不会得到任何的CPU运行
时间[1]。答案是否定的。从这些链表中删除线程是可能的,并且运行我们自己的调度程序来仅仅调度我们
自己的隐藏线程也是完全可能的。
2. NT调度程序工作方式的简单描述
-----------------------------------------------
接近第一阶段初始化结束的时候,MmInitSystem启动两个线程:KeBalanceSetManager
和 KeSwapProcessOrStack。这就是平衡集管理器(balance set manager)。
KeSwapProcessOrStack 启动一个处理交换(swap)事件的无限循环。交换事件由KiSwapEvent
触发。有四种不同类型的事件。
- 将内核堆栈交换出去(由BOOLEAN KiStackOutSwapRequest指定);
- 将进程交换出去 (需要交换出去的进程存放在KiProcessOutSwapListHead中)
- 将进程交换进来 (需要交换出去的进程存放在KiProcessInSwapListHead中)
- 将内核堆栈交换进来(需要交换进来的线程存放在KiStackInSwapListHead中).
KeBalanceSetManager也一直循环着并等待着一个MmWorkingSetManagerEvent事件(当内存低时
调整工作集的大小)和另一个定时器。定时器事件处理程序周期性地将KiStackOutSwapRequest设置为
TRUE,并且触发KiSwapEvent信号通知KeSwapProcessOrStack线程,KeSwapProcessOrStack 线程
不得不将长时间等待某个东西的线程的内核堆栈交换出去。KeBalanceSetManager也调用KiScanReadyQueues
来提高在就绪队列中线程(KiDispatcherReadyListHead数组)的优先级。对于每一个提高了优先级的线程,
KiReadyThread将会被调用,所以马上将PRCB.NextThread设置为提高了优先级的线程也是很有可能的
(KiReadyThread 会抢占原先的NextThread)。
KeUpdateSystemTime 直接由HAL的定时中断处理程序调用。它随后调用KeUpdateRunTime,
“更新当前线程的运行时间,当前线程所属进程的运行时间和减少当前线程的时间片”。
当KeUpdateRunTime注意到当前线程不是Idle线程并且它的时间片用完了,它通过触发一个调度中断
请求时间片结束。
KiDispatchInterrupt检查是否请求了时间片结束或已经选择了PRCB.NextThread。如果是,
它设置PRCB.CurrentThread指向PRCB.NextThread,将PRCB.NextThread 清0,最后通过调用
KiReadyThread 让 PRCB.CurrentThread 就绪运行。
KiReadyThread 检查线程所属进程的状态,如果进程已经被交换出去,就将它的内存交换
进来(他将进程插入到KiProcessInSwapListHead中并触发KiSwapEvent事件)。如果线程的内核
堆栈不驻留在内存的,KiReadyThread将线程插入到KiStackInSwapListHead中,触发KiSwapEvent
将线程内核堆栈交换进来。
如果线程所属的进程内存和线程的内核堆栈都是驻留的,KiReadyThread寻找一个空闲的
处理器,如果至少有一个空闲的处理器,它设置(每个空闲处理器)各自的PRCB的NextThread字段
到特定的线程。如果有很多个空闲的处理器,线程想要运行的处理器将成为优先选择。
如果没有空闲的处理器,将检查IdealProcessorPRCB.NextThread优先级。如果
IdealProcessorPRCB.NextThread是可以抢占的(指定的线程具有较高的优先级),KeReadyThread
设置它为指定的线程。如果IdealProcessorPRCB.NextThread没有被设置,KiReadyThread检查
IdealProcessorPRCB.CurrentThread是否能抢占。如果可以,NextThread就被设置为指定的线程,
再次请求调度中断。
如果没有线程可以被抢占,KiReadyThread 根据线程的优先级,将其插入到调度程序队列
(KiDispatcherReadyListHead)中去,修正各自的KiReadySummary位,表示这个优先级数组非空。