线程是系统处理器调度的基本单元,而且线程调度是在内核层完成的,所以,KTHREAD 的许多域都跟Windows 的线程调度机制有关.
找到进程的线程可以使用!process
1 2 3 | |
也可以根据KPROCESS的ThreadListHead来反推,这里仅找第一个,因为calc就一个线程
1 2 3 4 5 6 7 8 | |
1 | |
说明了内核层的线程对象同样是一个分发器对象,线程可以被等待,当线程结束时,它变成有信号状态
1 | |
由于突变体对象是有所有权的,一旦被某个线程等到,则其所有权归该线程所有,它也被连接到MutantListHead 链表中。
对应KMUTANT中的MutantListEntry
View Code CPP
1 2 3 | |
后面有个StackBase记录了当前栈的基位置(高地址),在线程初始化时,InitialStack 和StackBase是相等的,都指向原始的内核栈高地址。
1 2 | |
1 | |
1 2 3 4 5 6 7 8 9 10 11 | |
1 | |
1 | |
1 | |
View Code CPP
示例图如下:
在一个线程的生命周期中,它从完成初始化开始进入到线程调度器的视野中,之后,一直到完成所有预定的功能,最后终止并被销毁。在此过程中,为了获得系统的处理器资源,它必须要经历上图中的状态转移。
1 | |
1 2 3 4 5 6 | |
Alertable域说明了一个线程是否可以被唤醒,当一个线程正在等待时,如果它的Alertable值为TRUE,则它可以被唤醒。WaitNext 域也是一个布尔值,TRUE 值表示这个线程马上要调用一个内核等待函数,它的用途是,在发出了一个信号(比如释放了一个信号量对象)以后,接下来该线程会马上调用等待函数,所以,它不必解除线程调度器锁。WaitIrql 域与WaitNext 一起使用,当WaitNext 为TRUE 时,WaitIrql 记录了原先的IRQL值。WaitReason 域记录了一个线程的等待理由,其值的含义见base\ntos\inc\ke.h 中的KWAIT_REASON 枚举类型。WaitReason 基本上只是记录了等待的理由,而并不参与到线程调度或决策中。WaitMode 域记录了当线程等待时的处理器模式,即内核模式或用户模式的等待。接下来是WaitStatus 域,它记录了等待的结果状态.
1 2 | |
WaitBlockList 成员指向一个以KWAIT_BLOCK 为元素的链表,其中的KWAIT_BLOCK 对象指明了哪个线程在等待哪个分发器对象。对于一个线程而言,WaitBlockList域以及每个KWAIT_BLOCK 对象中的WaitListEntry 域构成了一个双链表,指明了该线程正在等待哪些分发器对象.
当一个线程正在等待门对象(Gate Object,也是一种分发器对象)时,GateObject 域记录了正在等待的门对象。由于等待门对象和等待其他分发器对象是不会同时发生的,所以,GateObject 和WaitBlockList 域构成了一个union,共用一个指针内存.
1 2 | |
1 2 3 | |
1 2 | |
当一个线程正在等待被执行时,WaitListEntry 作为一个线程节点加入到某个链表中。例如,在进程被换入内存过程中,就绪状态的线程将被加入到以进程的ReadyListHead域为链表头的双链表中,链表中的节点即为线程的WaitListEntry 域。SwapListEntry 域则被用于当线程的内核栈需要被换入时,插入到以全局变量KiStackInSwapListHead 为链表头的单链表中。另外,当一个线程处于DeferredReady状态时,其SwapListEntry 将被插入到某个处理器的DeferredReadyListHead 链表中(参见KiInsertDeferredReadyList 和KiProcessDeferredReadyList内核函数).
1 | |
1 | |
View Code CPP
KernelApcDisable 和SpecialApcDisable 都是16 位的整数值,0 表示不禁止APC,负数表示禁止APC,一个线程在执行过程中可以有多种因素要禁止APC,这些因素以负值来表示,并累加起来,当因素消除的时候再减去相应的负值。只有当KernelApcDisable 或SpecialApcDisable 为0 的时候,该线程才允许插入或提交APC。这两个值分别控制普通的内核APC 和特殊的内核APC.
1 | |
Teb 域是一个特殊的域,它指向进程地址空间中的一个TEB(线程环境块)结构。TEB结构包含了在用户地址空间中需要访问的各种信息,例如与线程相关的GDI 信息、系统支持的异常,甚至还有WinSock 的信息,等等.
View Code CPP
一个线程上的定时器,当一个线程在执行过程中需要定时器时,比如实现可超时的等待函数(KeWaitForSingleObject 或KeWaitForMultipleObjects),就会用到此定时器对象。紧接着Timer 域之后的是AutoAlignment 和DisableBoost两个位标记,线程的这两个标记直接继承自所属进程的同名标记.
View Code CPP
WaWaitBlock 域是一个包含4 个KWAIT_BLOCK 成员的数组,其中第4项专门用于可等待的定时器对象.如肯WaitBlockList所述,KWAIT_BLOCK 结构代表了一个线程正在等待一个分发器对象,或者说一个分发器对象正在被一个线程等待,它会被同时加入到两个双链表结构中.
WaitBlock 域是一个内置数组,内核在实现等待功能的时候,如果一个线程所等待的对象数量小于4(实际上应该是3 个分发器对象加上一个定时器对象),则内核无须另外分配KWAIT_BLOCK 对象内存,只需直接使用WaitBlock 中的数组成员即可。如果等待的对象数量大于4,则内核必须分配额外的KWAIT_BLOCK对象内存。由于等待操作在内核中非常频繁,所以,利用静态数组来满足大多数情况下的内存需求, 这一优化非常有意义.
1 | |
1 | |
TrapFrame 域是一个线程中最为关键的部分了。在Windows 中,线程是系统调度的基础,它代表了一个进程中的一个控制流。当一个线程离开运行状态时,其当前的执行状态,比如现在的指令指针(IP)在哪里,各个寄存器中的值是什么,等等,都必须保存下来,以便下次再轮到这个线程运行时,可以恢复原来的执行状态。TrapFrame 域正是记录控制流状态的数据结构,它是一个指向KTRAP_FRAME 类型的指针,它包含了Intel x86 的所有常用寄存器.
View Code CPP
1 | |
1 | |
ServiceTable 域指向该线程使用的系统服务表(全局变量KeServiceDescriptorTable),如果这是一个图形用户界面(GUI)线程,此域将指向另一个影子系统服务表(全局变量KeServiceDescriptorTableShadow),这里是calc,所以为ShadowSSDT
View Code CPP
1 | |
1 2 3 | |
Preempted域说明这个线程是否被高优先级的线程抢占了,只有当一个线程正在运行或者正在等待运行而被高优先级线程抢占的时候,此值才会置成TRUE。在其他情况下,该值总是FALSE.
ProcessReadyQueue域说明一个线程是否在所属进程KPROCESS对象的ReadyListHead 链表中,TRUE表示在此链表中,FALSE表示不在.
KernelStackResident域说明该线程的内核栈是否驻留在内存中,当内核栈被换出内存时,该值将被置成FALSE;当换入内存时,再置成TRUE.
1 2 3 | |
View Code CPP
ApcStateIndex 域是一个索引值,它指明了当前的APC 状态在ApcStatePointer 域中的索引。由于ApcStatePointer 是一个只有两个元素的数组,所以,ApcStateIndex 的值为0或者1。ApcStatePointer 数组元素的类型是指向KAPC_STATE 的指针,其两个元素分别指向线程对象的ApcState 和SavedApcState 域.
View Code CPP
Win32Thread 域是一个指针,指向由Windows 子系统管理的区域。SuspendApc 和SuspendSemaphore 两个union 域相互之间有联系,SuspendApc 被初始化成一个专门的APC。当该APC 被插入并交付时,KiSuspendThread 函数被执行,其执行结果是在线程的SuspendSemaphore 信号量上等待,直到该信号量对象有信号,然后线程被唤醒并继续执行。线程的挂起(suspend)操作正是通过这一机制来实现的,参见base\ntos\ke\thredobj.c 中的KeSuspendThread 和KeFreezeAllThreads 函数。自然地,线程的恢复(resume)操作则是通过控制SuspendSemaphore 信号量的计数来实现的,参见base\ntos\ke\thredobj.c 中的KeResumeThread 等函数。
1 2 | |
线程对象提供了为参与线程调度而必需的各种信息及其维护控制流的状态.