【内核原理与实现 005】KTHREAD 结构体属性介绍
程序员文章站
2022-07-13 10:52:12
...
typedef struct _KTHREAD {
//
// The dispatcher header and mutant listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header; // KTHREAD 是可等待对象,线程结束时有信号
// 此链表存储了属于该线程的所有 mutant(对应3环的 mutex)
// 一旦该线程获得了 mutex ,则 mutex 挂入该链表
LIST_ENTRY MutantListHead;
//
// The following fields are referenced during context switches and wait
// operatings. They have been carefully laid out to get the best cache
// hit ratios.
// 下面的域布局精心设计以提高 cache 命中率
PVOID InitialStack; // 原始栈底
PVOID StackLimit; // 栈顶的最低地址(栈界限)
PVOID KernelStack; // 内核栈顶esp,线程切换时保存esp到这里
// 自旋锁对象,用于保护线程数据成员
KSPIN_LOCK ThreadLock;
// KAPC_STATE 存储了该线程的APC信息,包括用户/内核APC链表,当前所属进程(父进程或挂靠进程)
// 是否正在执行内核APC,是否有要执行的用户/内核APC
union {
KAPC_STATE ApcState;
struct {
// 用来占用一个 KAPC_STATE 的大小
UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
BOOLEAN ApcQueueable; // 是否可以插入APC
volatile UCHAR NextProcessor; // 处理器调度相关
volatile UCHAR DeferredProcessor; // 处理器调度相关
UCHAR AdjustReason; // 优先级调整的原因
SCHAR AdjustIncrement; // 优先级调整量
};
};
// 自旋锁对象,用于保护APC队列操作
KSPIN_LOCK ApcQueueLock;
#if !defined(_AMD64_)
ULONG ContextSwitches; // 记录线程切换次数
volatile UCHAR State; // 9种线程状态,见 _KTHREAD_STATE ,1就绪,2运行,5等待
UCHAR NpxState; // 浮点寄存器状态,本书不讨论
KIRQL WaitIrql; // 配合 WaitNext 使用,详见 WaitNext 注释
KPROCESSOR_MODE WaitMode; // 线程等待时的处理器模式,0内核,1用户
#endif
LONG_PTR WaitStatus; // 等待的结果状态
union {
// 指向一个以 KWAIT_BLOCK 为元素的链表
// KWAIT_BLOCK 对象指明了哪个线程在等待哪个分发器对象
// 对于分发器对象,它又有另一个 KWAIT_BLOCK 链表指明哪些线程正在等待它
// 细节要等到 5.4 节介绍线程同步机制时才能知道
PKWAIT_BLOCK WaitBlockList;
// 记录了正在等待的门对象(也是一种分发器对象)
PKGATE GateObject;
// 等待门对象和等待其他分发器对象不会同时发生,所以放在一个共用体里了
};
BOOLEAN Alertable; // 线程等待时,是否可以被唤醒
// WaitNext == TRUE 表示这个线程马上要调用一个内核等待函数,所以不必解除线程调度器锁
// WaitNext == TRUE 时,WaitIrql 记录了原先的 IRQL 值。
BOOLEAN WaitNext;
// 记录线程等待的原因,见 KWAIT_REASON 枚举类型,但不参与线程调度或决策
UCHAR WaitReason;
SCHAR Priority; // 动态优先级值
UCHAR EnableStackSwap; // 本线程的内核栈是否允许被换出内存
volatile UCHAR SwapBusy; // 本线程当前是否正在进行上下文切换
BOOLEAN Alerted[MaximumMode]; // 长度为2的数组,指定该线程在内核/用户模式是否可以被唤醒
union {
// 线程等待被执行时,通过此节点挂入 ThreadListEntry 链表(KPROCESS/EPROCESS成员)
LIST_ENTRY WaitListEntry;
// 当线程的内核栈需要被换入时,插入到全局链表 KiStackInSwapListHead 中
// 当线程状态为 DeferredReady 时,插入到某个CPU的 DeferredReadyListHead 链表中
SINGLE_LIST_ENTRY SwapListEntry;
};
// 队列分发器对象,如果不为NULL,说明当前线程正在处理此队列对象中的项
PRKQUEUE Queue;
#if !defined(_AMD64_)
// 记录该线程进入等待时刻的时间点(时钟滴答值的低32位),
// 用于平衡集管理器根据线程等待时间的先后来做一些决策,详见第4章
ULONG WaitTime;
union {
struct {
// 0表示不禁用APC,负数表示禁用APC
// 线程执行过程中,有多种因素要禁止APC,这些因素以负值累加,
// 当因素消除时,再减去相应的负值,只有当 KernelApcDisable / SpecialApcDisable
// 等于0时,才允许插入或提交APC
// 禁用普通内核APC
SHORT KernelApcDisable;
// 禁用特殊内核APC
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
#endif
// 指向进程地址空间中的一个TEB(线程环境块)结构
// TEB 包含了3环需要访问的各种信息,如线程相关的GDI信息、系统支持的异常等
// TEB 定义在 public\sdk\inc\pebteb.h
PVOID Teb;
union {
// 线程定时器,用于实现可超时的等待,如 KeWaitForSingleObject 会用到此定时器对象
KTIMER Timer;
struct {
UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];
//
// N.B. The following bit number definitions must match the
// following bit field.
//
// N.B. These bits can only be written with interlocked
// operations.
//
#define KTHREAD_AUTO_ALIGNMENT_BIT 0
#define KTHREAD_DISABLE_BOOST_BIT 1
union {
struct {
LONG AutoAlignment : 1; // 继承自 KPROCESS
LONG DisableBoost : 1; // 继承自 KPROCESS
LONG ReservedFlags : 30;
};
LONG ThreadFlags;
};
};
};
union {
// 包含4个KWAIT_BLOCK的数组,其中第4项专门用于可等待的定时器对象
// KWAIT_BLOCK 结构代表一个线程正在等待一个分发器对象,或者说
// 一个分发器对象正在被一个线程等待,他会被同时加入到两个双链表结构中。
// 内核实现等待功能时,如果等待的对象数量少于4(3个分发器对象加上一个定时器对象),
// 则无需额外分配 KWAIT_BLOCK 对象的内存,只需直接使用 WaitBlock 数组即可。
// 如果等待的对象数量大于4,则内核必须分配额外的 KWAIT_BLOCK 对象内存。
// 等待操作在内核中非常频繁,所以利用静态数组来满足大多数情况下的内存需求,详见5.4节
KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
struct {
UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0];
BOOLEAN SystemAffinityActive;
};
struct {
UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1];
CCHAR PreviousMode;
};
struct {
UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2];
UCHAR ResourceIndex;
};
struct {
UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3];
UCHAR LargeStack;
};
#if defined(_AMD64_)
struct {
UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0];
ULONG ContextSwitches;
};
struct {
UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1];
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;
};
struct {
UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2];
ULONG WaitTime;
};
struct {
UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3];
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
};
#endif
};
// 记录了线程在处理一个队列项时挂入队列对象的线程链表中的节点地址,详见5.4.2节
LIST_ENTRY QueueListEntry;
//
// The following fields are accessed during system service dispatch.
//
// 指向 KTRAP_FRAME 的指针,KTRAP_FRAME 存储了 x86 所有常用寄存器
// KTRAP_FRAME 用于线程切换时保存寄存器
PKTRAP_FRAME TrapFrame;
// 线程的回调栈地址,此栈在该线程从0环回3环时使用
PVOID CallbackStack;
// 指向该线程使用的系统服务表(全局变量 KeServiceDescriptorTable)
// 如果这是一个GUI线程,那么此值指向影子系统服务表(KeServiceDescriptorTableShadow)
PVOID ServiceTable;
#if defined(_AMD64_)
ULONG KernelLimit;
#endif
//
// The following fields are referenced during ready thread and wait
// completion.
//
// 当前APC状态在 ApcStatePointer 数组中的索引
// 标识当前线程处于什么状态:0 正常状态 1 挂靠状态
UCHAR ApcStateIndex;
// 多处理器机器上该线程的理想处理器
UCHAR IdealProcessor;
// 是否被高优先级的线程抢占了,只有当一个线程正在运行或正在等待运行而被高优先级线程抢占,
// 这个值才置1,其他时候总是0
BOOLEAN Preempted;
// 线程是否在所属进程 KPROCESS 对象的 ReadyListHead 链表中
BOOLEAN ProcessReadyQueue;
#if defined(_AMD64_)
PVOID Win32kTable;
ULONG Win32kLimit;
#endif
// 内核栈是否驻留在内存中,内核栈换出内存时置0,换入内存时置1
BOOLEAN KernelStackResident;
// 静态优先级值,继承自 KPROCESS
// 可通过KeSetBasePriorityThread 函数重新设定
SCHAR BasePriority;
// 优先级动态调整过程中的递减值,参见 KiComputeNewPriority 函数
// 优先级分为两个区域,0-15是普通线程优先级;16-31是实时线程优先级,优先级调整不会跨区域。
SCHAR PriorityDecrement;
// Saturation 说明了线程基本优先级相对于进程基本优先级的调整量是否超过整个区间的一半
// 取值范围是 0, 1, -1
CHAR Saturation;
// 线程的用户亲和性,初始化时继承自 KPROCESS,可通过 KeSetAffinityThread 函数修改
KAFFINITY UserAffinity;
// 指向线程的父进程对象,线程初始化时指定,见 KeInitThread 函数
PKPROCESS Process;
// 该线程的CPU亲和性,线程初始化时继承自 KPROCESS
// 为线程指定的CPU集合必须是其进程的亲和性CPU集合的子集
// Affinity 的值可能有两种设置:一是系统亲和性,当该线程执行系统任务时通过
// KeSetSystemAffinityThread 函数来设置;二是线程本身的亲和性,称为用户
// 亲和性,通过 KeRevertToUserAffinityThread 函数来设置。
KAFFINITY Affinity;
//
// The below fields are infrequently referenced.
//
// 配合 ApcStateIndex 使用,ApcStatePointer[ApcStateIndex] 总是指向所属进程,
// 正常情况下所属进程是父进程,挂靠情况下所属进程指的是挂靠进程。
// 详细信息请参考 5.2.6节关于APC机制的介绍
PKAPC_STATE ApcStatePointer[2];
union {
KAPC_STATE SavedApcState;
struct {
UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
CCHAR FreezeCount;
CCHAR SuspendCount;
UCHAR UserIdealProcessor;
UCHAR CalloutActive;
#if defined(_AMD64_)
BOOLEAN CodePatchInProgress;
#elif defined(_X86_)
UCHAR Iopl;
#else
UCHAR OtherPlatformFill;
#endif
};
};
// 指向windows子系统管理的区域
PVOID Win32Thread;
// 当前栈底,线程初始化时,此值等于 InitialStack
PVOID StackBase;
union {
// 初始化成一个专门的APC,当该APC被插入并交付时,调用 KiSuspendThread 函数,
// 其执行结果是在线程的 SuspendSemaphore 信号量上等待,直到该信号量有信号,
// 然后该线程被唤醒,继续执行。线程的挂起操作正是通过这一机制实现的,详见
// KeSuspendThread 和 KeFreezeAllThreads 函数。
KAPC SuspendApc;
struct {
UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0];
SCHAR Quantum;
};
struct {
UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1];
UCHAR QuantumReset;
};
struct {
UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG];
ULONG KernelTime;
};
struct {
UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1];
PVOID TlsArray;
};
struct {
UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2];
PVOID BBTData;
};
struct {
UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH];
UCHAR PowerState;
ULONG UserTime;
};
};
union {
// 线程的恢复操作是通过控制 SuspendSemaphore 信号量的计数来实现的,
// 详见 KeResumeThread 等函数。
KSEMAPHORE SuspendSemaphore;
struct {
UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
// SListFaultCount 记录了在 SListFaultAddress 地址上发生页面错误的次数
ULONG SListFaultCount;
};
};
// 线程创建时,挂入到 KPROCESS 的 ThreadListHead 链表中
LIST_ENTRY ThreadListEntry;
// SListFaultAddress 记录了上一次用户模式互锁单链表 POP 操作发生页面错误的地址
PVOID SListFaultAddress;
#if defined(_WIN64)
LONG64 ReadOperationCount;
LONG64 WriteOperationCount;
LONG64 OtherOperationCount;
LONG64 ReadTransferCount;
LONG64 WriteTransferCount;
LONG64 OtherTransferCount;
#endif
} KTHREAD, *PKTHREAD, *PRKTHREAD;