Windows中进程与线程的设置

关于进程与线程

操作系统“复习”

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

进程和程序

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

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

image-20230614104209374

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

线程

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

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

内核层的进程和线程对象

在内核当中的描述为KPROCESSKTHREAD

在WRK中的定义为


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;

那么在线程当中


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:与上面有关。