一个EPROCESS块表示一个进程,位于系统空间,但EPB(环境进程块)位于用户空间中,因为其中有些信息需要用户模式的代码来修改。
EPROCESS的第一个成员为KPROCESS(内核进程块),内核进程块中存放着与调度有关的信息。
与进程有关的内核变量:
PsActiveProcessHead 进程块的列表头
PsIdleProcess 空闲(Idle)进程块
PsInitialSystemProcess 指向初始系统进程的进程块指针
CreateProcess流程:
1.打开映像文件,创建内存区对象;
2.创建Windows执行体进程对象;
3.创建初始线程(栈、执行环境、以及Windows执行体线程对象);
4.通知Windows子系统进程,为新进程做好准备;
5.开始执行初始线程;
6.完成地址空间初始化(比如加载必要的DLL),调用映像入口点开始执行。
在阶段1找到有效的Windows可执行映像后,如果HKML\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options下有相同名字的子键,并且存在Debugger的值,则替换加载该值的映像。
阶段2调用NtCreateProcess系统函数,建立EPROCESS,创建初始的进程地址空间,初始化内核进程块(KPROCESS),建立PEB。
阶段3中建立初始线程的启动函数(保存在ETHREAD中),是位于Kernel32.dll中的BaseProcessStart(该进程中以后创建线程的启动函数为BaseThreadStart)。
阶段6中,内核线程准备返回用户模式时,首先会执行已经插入的APC队列(Ntdll.dll的LdrInitializeThunk),LdrInitializeThunk例程初始化加载器、堆管理器、NLS表、线程局部存储区(TLS)数组,以及临界区结构,加载必要的DLL,以DLL_PROCESS_ATTACH调用各DLL入口点,最后返回用户模式调用线程的启动函数。
一个ETHREAD块表示一个线程,位于系统空间,但TEB(线程环境块)位于用户空间中。
CreateThread创建线程过程:
1.在进程地址空间为新线程创建一个用户栈;
2.初始化线程的硬件环境(CONTEXT结构);
3.调用NtCreateThread创建挂起状态的执行体线程对象;
4.通知Windows子系统新线程消息;
5.线程句柄、ID返回调用者;
6.被调度执行。
线程优先级:实时级别(16-31)、可变级别(1-15)、系统级别保留给零页面线程(0)。
线程状态:就绪(Ready)、备用(Standby)、运行(Running)、等待(Waiting)、转换(Transition)、终止(Terminated)、初始(Initialized)。
分发器的就绪队列(ready queue)(KiDispatcherReadyListHead)包含了32个优先级别处于就绪状态的线程队列,就绪摘要(ready summary)(KiReadySummary)是一个32位的掩码,每一位代表了每个优先级就绪队列是否存在就绪线程。
一个线程运行的时限到了,分发器检测是否还有另一个同样优先级的线程正在等待运行,如果没有则该线程可以再运行一个时限。2K和XP上,线程的默认时限为2个时钟间隔,一般单处理器上时钟间隔是10毫秒左右,多处理器在15毫秒左右。
系统默认将前台进程中线程的时限变为原有的3倍,所以相同优先级的前台进程会比后台进程获得更多的CPU时间。
环境切换:典型的线程切换需要保存和加载指令指针、用户栈和内核栈指针、页目录指针。
以下情形中,Windows可以提升线程的当前优先级:
I/O操作完成时;
等待执行体事件或信号量以后;
在前台进程中的线程完成了一个等待操作以后;
GUI线程由于窗口活动被唤醒时;
一个准备运行的线程在一段时间内还没有被运行起来(CPU饥饿)。
注:线程的当前优先级提升最大不会超过15,这些线程每运行完一个时限优先级减1,直至还原到基本优先级。
多处理器的线程调度算法:
一个线程被创建时会分配一个理想处理器,理想处理器由进程块的一个种子来选定,每次新线程创建该种子被递增,所以一个进程中每个新线程会循环经历所有可用处理器。(注:根据下面的调度规则看来,当改变一个线程的亲和性时,线程的理想处理器也会随之更改)
a)存在空闲处理器:
2K下当一个线程准备好运行时,Windows第一优先选择线程的理想处理器,然后是线程的上一个处理器,接着是当前正在执行的处理器(正在运行调度代码的CPU),最后是空闲处理器掩码中第一个可用的最高编号处理器。
XP和2K3中有些不同,首先空闲处理器掩码集合被设置为该线程亲和性掩码允许的空闲处理器。如果当前处理器在这个空闲处理器集合中,则线程调度到此处理器上运行。否则检查上一个处理器是否在集合中,找不到的话最后排除所有睡眠状态中的CPU(前提排除后还存在空闲处理器),选择空闲处理器集合中最低编号的处理器。
一旦为线程选择了一个处理器,线程被置入备用(Standby)状态,处理器的PRCB随之被更新,指向该线程。当该处理器上的空闲循环运行时,线程被选择出来得到运行。
b)不存在空闲处理器:
XP和2K3,与线程的理想处理器上运行的线程比较优先级,高于则将一个跨处理器的中断插入到目标处理器队列中,从而抢占运行,否则就加入到就绪队列中等待被调度。
2K下,选择处理器比较优先级时,会检查是否在线程的亲和性掩码中,依次按照理想处理器、上一个处理器,如果这些处理器亲和性掩码都不允许,最后选择排除亲和性的处理器集合中最高编号的处理器。