Joe1sn's Cabinet

【win内核原理与实现】II. 进程与线程

Windows中进程与线程的设置

关于进程与线程

操作系统“复习”

​ 在学习操作系统的时候,有个重点就是进程与线程的区别。最开始进程和线程是没有分开的,由于多数操作系统分开了用户态和内核态,那么用户态就必须和内核态进行交互才能调用系统资源(通过IOCTL交互)。这样每一个进程在内核当中都有一个“进程描述符”的东西来描述这个进程,并根据调度算法完成进程的运行。这里就假设一个进程test.exe调用了0x40大小的内存空间,在只有进程的时候,进程描述符中就会标记这个内存被这个进程使用,同时指令和运行都在其中。
​ 这样有个问题就是,我需要两个这种“进程”来运行,且他们需要的恰好是同一块内存的相同数据,如果只有进程的话,就会存在两个0x40的内存,同时造成不必要的复制粘贴。随后就有了线程这个概念,比如面对上述情况,进程就只含有一个0x40大小的内存,对内存的访问就交给这个线程对应的进程。

进程和程序

​ Windows的任务调度算法可以很好地适应多处理器和多任务的情形,在windows中的进程也遵守上述的准则。对于Windows内核需要做的事情是:维护一个全局的进程表,记录下当前有哪些进程正 在被执行;把时间分成适当的片段,在现代处理器结构中,这可以通过设置时钟中断来完成,因而每次时钟中断到来时系统就会获得控制权;在进程间实施切换,即保留上一个进 程的环境信息,恢复下一个进程的执行环境。关于Windows的调度算法可以简单理解为时间轮。

​ 如果程序是一个完全的模块,那么他的内存就是我们熟悉的经典内存结构。

image-20230614104209374

​ 但是往往一个最简单的helloworld也会调用CRunTime的代码,所以有进程就能使用 共享内存 ,比如在静态数据区有着一份复制。

线程

​ 线程不仅仅是一个控制流,它还有更多的内容。线程的调用栈(call stack)记录了它 作为控制流的状态信息,包括每一层函数调用和返回的指令地址。线程一定隶属于某个进 程,其控制流可以访问这个进程中的资源,包括所有的内存数据以及系统分配给此进程的 其他资源。一个进程可以有多个线程,由于这些线程隶属于同一个进程,所以它们之间相 互通信要方便得多,毕竟几乎所有的资源(并非全部)对于它们来说都是共享的。

Windows 中进程和线程的数据结构

内核层的进程和线程对象

在内核当中的描述为KPROCESSKTHREAD

在WRK中的定义为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

typedef struct _KPROCESS
{
DISPATCHER_HEADER Header; //表明是分发器对象,可用于等待。进程退出时,此对象为有信号状态
LIST_ENTRY ProfileListHead; //进程参与性能分析时,作为节点加入全局性能分析进程链表。
ULONG DirectoryTableBase; // 两个成员的数组,第一个指向页目录表地址,第二个指向超空间的页目录表地址
ULONG Unused0;
KGDTENTRY LdtDescriptor; //LDT的描述符
KIDTENTRY Int21Descriptor; //为了兼容DOS,通过int 21h调用系统功能
WORD IopmOffset; //指定IOPM(IO权限表,IO Privilege Map)位置。控制进程的用户模式IO访问权限
UCHAR Iopl; //IO优先级(IO Privilege Level)
UCHAR Unused;
ULONG ActiveProcessors; //记录进程正在哪些处理器上运行
ULONG KernelTime; //在内核模式运行所花时间
ULONG UserTime; //在用户模式运行所花时间
LIST_ENTRY ReadyListHead; //保存进程中处于就绪状态但未被加入全局就绪链表的线程
SINGLE_LIST_ENTRY SwapListEntry; //进程要被换出时,通过此域加入到KiProcessOutSwapListHead为头的单链表
PVOID VdmTrapcHandler; //VDM环境下运行16位程序时,处理Ctrl+C中断的函数
LIST_ENTRY ThreadListHead; //指向一个链表头,链表中包含该进程的所有线程
ULONG ProcessLock; //一个自旋锁对象。保证对进程数据结构中成员的互斥访问
ULONG Affinity; //指定该进程的线程可以在哪些处理器上运行
union
{
ULONG AutoAlignment: 1;
ULONG DisableBoost: 1; //
ULONG DisableQuantum: 1;
ULONG ReservedFlags: 29;
LONG ProcessFlags;
};
CHAR BasePriority; //该进程的线程的基本优先级
CHAR QuantumReset; //进程中线程的基本时限重置值
UCHAR State; //说明进程是否在内存中
UCHAR ThreadSeed; //该进程的下一个创建线程的理想处理器
UCHAR PowerState; //电源状态
UCHAR IdealNode; //进程优先选择的处理器节点
UCHAR Visited;
union
{
KEXECUTE_OPTIONS Flags;
UCHAR ExecuteOptions; //NX执行选项
};
ULONG StackCount; //当前进程中有多少个线程的栈位于内存中
LIST_ENTRY ProcessListEntry; //当前系统中所有具有活动线程的进程通过这个域串成一个链表
UINT64 CycleTime;
} KPROCESS, *PKPROCESS;

那么在线程当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

typedef struct _KTHREAD
{
DISPATCHER_HEADER Header;
UINT64 CycleTime;
ULONG HighCycleTime;
UINT64 QuantumTarget;
PVOID InitialStack;
PVOID StackLimit;
PVOID KernelStack;
ULONG ThreadLock;
union
{
KAPC_STATE ApcState;
UCHAR ApcStateFill[23];
};
CHAR Priority;
WORD NextProcessor;
WORD DeferredProcessor;
ULONG ApcQueueLock;
ULONG ContextSwitches;
UCHAR State;
UCHAR NpxState;
UCHAR WaitIrql;
CHAR WaitMode;
LONG WaitStatus;
union
{
PKWAIT_BLOCK WaitBlockList;
PKGATE GateObject;
};
union
{
ULONG KernelStackResident: 1;
ULONG ReadyTransition: 1;
ULONG ProcessReadyQueue: 1;
ULONG WaitNext: 1;
ULONG SystemAffinityActive: 1;
ULONG Alertable: 1;
ULONG GdiFlushActive: 1;
ULONG Reserved: 25;
LONG MiscFlags;
};
UCHAR WaitReason;
UCHAR SwapBusy;
UCHAR Alerted[2];
union
{
LIST_ENTRY WaitListEntry;
SINGLE_LIST_ENTRY SwapListEntry;
};
PKQUEUE Queue;
ULONG WaitTime;
union
{
struct
{
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
PVOID Teb;
union
{
KTIMER Timer;
UCHAR TimerFill[40];
};
union
{
ULONG AutoAlignment: 1;
ULONG DisableBoost: 1;
ULONG EtwStackTraceApc1Inserted: 1;
ULONG EtwStackTraceApc2Inserted: 1;
ULONG CycleChargePending: 1;
ULONG CalloutActive: 1;
ULONG ApcQueueable: 1;
ULONG EnableStackSwap: 1;
ULONG GuiThread: 1;
ULONG ReservedFlags: 23;
LONG ThreadFlags;
};
union
{
KWAIT_BLOCK WaitBlock[4];
struct
{
UCHAR WaitBlockFill0[23];
UCHAR IdealProcessor;
};
struct
{
UCHAR WaitBlockFill1[47];
CHAR PreviousMode;
};
struct
{
UCHAR WaitBlockFill2[71];
UCHAR ResourceIndex;
};
UCHAR WaitBlockFill3[95];
};
UCHAR LargeStack;
LIST_ENTRY QueueListEntry;
PKTRAP_FRAME TrapFrame;
PVOID FirstArgument;
union
{
PVOID CallbackStack;
ULONG CallbackDepth;
};
PVOID ServiceTable;
UCHAR ApcStateIndex;
CHAR BasePriority;
CHAR PriorityDecrement;
UCHAR Preempted;
UCHAR AdjustReason;
CHAR AdjustIncrement;
UCHAR Spare01;
CHAR Saturation;
ULONG SystemCallNumber;
ULONG Spare02;
ULONG UserAffinity;
PKPROCESS Process;
ULONG Affinity;
PKAPC_STATE ApcStatePointer[2];
union
{
KAPC_STATE SavedApcState;
UCHAR SavedApcStateFill[23];
};
CHAR FreezeCount;
CHAR SuspendCount;
UCHAR UserIdealProcessor;
UCHAR Spare03;
UCHAR Iopl;
PVOID Win32Thread;
PVOID StackBase;
union
{
KAPC SuspendApc;
struct
{
UCHAR SuspendApcFill0[1];
CHAR Spare04;
};
struct
{
UCHAR SuspendApcFill1[3];
UCHAR QuantumReset;
};
struct
{
UCHAR SuspendApcFill2[4];
ULONG KernelTime;
};
struct
{
UCHAR SuspendApcFill3[36];
PKPRCB WaitPrcb;
};
struct
{
UCHAR SuspendApcFill4[40];
PVOID LegoData;
};
UCHAR SuspendApcFill5[47];
};
UCHAR PowerState;
ULONG UserTime;
union
{
KSEMAPHORE SuspendSemaphore;
UCHAR SuspendSemaphorefill[20];
};
ULONG SListFaultCount;
LIST_ENTRY ThreadListEntry;
LIST_ENTRY MutantListHead;
PVOID SListFaultAddress;
PVOID MdlForLockedTeb;
} KTHREAD, *PKTHREAD;

Header:说明该对象是一个分发器对象,可以被等待。线程结束时,等待被满足。

MutantListHead:指向一个链表头。链表中包含所有属于该线程的突变体对象(mutant,对应互斥体对象)。

InitialStack:原始栈位置(高地址)

StackLimit:栈低地址

KernelStack:内核调用栈开始位置

StackBase:当前栈的基地址。

ThreadLock:自旋锁,用于保护线程数据成员。

ApcState:KAPC_STATE结构,指定线程的APC信息,包括APC链表,是否有APC正在等待,是否正在处理APC。

ApcQueueable:是否可插入APC

NextProcessor:关于处理器调度的选择。

DeferredProcessor:关于处理器调度的选择。

AdjustReason:优先级调整原因

AdjustIncrement:优先级调整调整量

ApcQueueLock:保护APC队列的自旋锁。

ContextSwitches:记录线程进行了多少次切换。

State:线程当前状态。

NpxState:浮点处理器状态。

Alertable:线程是否可以被唤醒。

WaitNext:

WaitIrql:原先的IRQL。

WaitReason:等待原因

WaitMode:线程等待时的处理器模式,内核or用户

WaitStatus:等待的结果状态。

WaitBlockList:KWAIT_BLOCK为元素的链表,记录线程所有等待的分发器对象。每个分发器对象也有一个KWAIT_BLOCK组成的链表,记录所有等待在该对象的线程。

GateObject:等待的门对象,等待门对象和等待分发器对象不会同时发生。

Priority:动态优先级。

BasePriority:基本优先级。

PriorityDecrement:优先级动态调整过程中的递减值。

Saturation:线程基本优先级调整相对于进程基本优先级是否超过了区间的一半。

EnableStackSwap:内核栈是否准许被换出。

SwapBusy:当前是否正在进程上下文切换。

Alerted:线程在警告模式下是否可以被唤醒。

WaitListEntry:双向链表节点,等待被执行时,作为节点加入某链表

SwapListEntry:单链表节点,内核栈需要被换出时,加入KiStackInSwapListHead为头的链表。另外,线程处于DeferredReady状态时加入DeferredReadyListHead为头的链表。

Queue:队列分发器对象,线程正在处理此队列中的项。

WaitTime:线程进入等待时刻的时间点。

KernelApcDisable/SpecialApcDisable:内核APC和特殊内核APC是否被禁止。

TEB:进程地址空间的一个TEB域

Timer:定时器。

AutoAlignment:与KPROCESS相同

DisableBoost:与KPROCESS相同

WaitBlock:4个KWAIT_BLOCK成员的数组,线程等待的分发器少于4个时,使用这里的空间,不分配新空间。

QueueListEntry:线程处理一个队列项时,加入到队列对象的线程链表中的地址。

TrapFrame:指向KTRAP_FRAME类型的指针。用户保存执行现场。

CallbackStack:线程的回调栈地址,在从内核模式返回用户模式时用。

ServiceTable: 指向系统使用的系统服务表,非GUI线程为KeServiceDescriptorTable,GUI线程为KeServiceDescriptorTableShadow。

IdealProcess:理想处理器

Preempted:是否被高优先级线程抢占了。

ProcessReadyQueue:是否在进程对象的ReadyListHead列表中。

KernelStackResident:线程的内核栈是否驻留在线程中。

Affinity:处理器亲和性,为线程指定的处理器集合必须是该集合的子集。

UserAffinity:线程的用户亲和性。

Process:执行线程的进程对象。

ApcStateIndex:指明当前APC状态在ApcStatePointer域中的索引。

Win32Thread:指向Windows子系统管理的区域的指针。

SuspendApc/SuspendSemaphore:用于支持线程挂起的域。

ThreadListEntry:双链表的节点,线程被创建时,加入到进程的ThreadListHead链表中。

SListFaultAddress:上一次用户模式互锁单链表POP操作发生页面错误的地址。

SuspendSemaphore:与上面有关。