Windows中進程和線程的數據結構
Windows內核中的執行體層負責各種與管理和策略相關的功能,而內核層(或微內核)實現了操作系統的核心機制。進程和線程在這兩層上都有對應的數據結構。
內核層的進程和線程對象
首先看(微)內核層的數據結構,這是進程和線程最爲基本的數據結構,分別名爲KPROCESS和KTHREAD。
KPROCESS的定義(見base\ntos\inc\ke.h)
typedef struct _KPROCESS {
//
// The dispatch header and profile listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header; //表明KPROCESS對象也是一個分發器對象(dispatcher object),進程對象本身是可以被等待的
LIST_ENTRY ProfileListHead; //用於當該進程參與性能分析(profiling)時,作爲一個節點加入到全局的性能分析進程列表(內核全局變量KiProfileListHead)中。
//
// The following fields are referenced during context switches.
//
ULONG_PTR DirectoryTableBase[2]; //是一個只有兩項的數組,其中第一項指向該進程的頁目錄表地址,第二項指向該進程的超空間(hyper space)的頁目錄表地址。
#if defined(_X86_)
//接下來的LdtDescriptor、Int21Descriptor、IopmOffset和Iopl這四個域是專門針對Intel x86處理器的。
KGDTENTRY LdtDescriptor; //進程的LDT(局部描述符表)的描述符
KIDTENTRY Int21Descriptor; //是爲了兼容DOS程序,允許它們通過int 21h指令來調用DOS系統功能
USHORT IopmOffset; //指定了IOPM(I/O權限表,I/O Privilege Map)的位置,內核通過IOPM可控制進程的用戶模式I/O訪問權限
UCHAR Iopl; //定義了進程的I/O優先級(I/O Privilege Level)
BOOLEAN Unused;
#endif
#if defined(_AMD64_)
USHORT IopmOffset;
#endif
volatile KAFFINITY ActiveProcessors;//記錄了當前進程正在哪些處理器上運行
//
// The following fields are referenced during clock interrupts.
//
ULONG KernelTime; //進程對象在內核模式下所花的時間
ULONG UserTime; //進程對象在用戶模式下所花的時間
//
// The following fields are referenced infrequently.
//
LIST_ENTRY ReadyListHead; //R一個雙向鏈表的表頭,該鏈表記錄了這個進程中處於就緒狀態但尚未被加入全局就緒鏈表的線程
SINGLE_LIST_ENTRY SwapListEntry; //個單鏈表項,當一個進程要被換出內存時,它通過此域加入到以KiProcessOutSwapListHead爲鏈頭的單鏈表中;當一個進程要被換入內存時,它通過此域加入到以KiProcessInSwapListHead爲鏈頭的單鏈表中。
#if defined(_X86_)
PVOID VdmTrapcHandler; //指向處理Ctrl+C中斷的函數,僅用於在VDM(虛擬DOS機,Virtual DOS Machine)環境下運行16位程序
#else
PVOID Reserved1;
#endif
LIST_ENTRY ThreadListHead; //指向一個鏈表頭,此鏈表包含了該進程的所有當前線程。當一個線程被初始創建的時候,被加入到此鏈表中,在終止的時候被從鏈表中移除。由此我們可以看出進程和線程之間的從屬關係
KSPIN_LOCK ProcessLock; //ProcessLock域,這是一個自旋鎖(spin lock)對象,它的用途是保護此進程中的數據成員。
KAFFINITY Affinity; //Affinity域指定了該進程的線程可以在哪些處理器上運行,其類型是KAFFINITY,這是一個32位或64位整數,其二進制表示的每一位分別對應於當前機器上的一個處理器(或核)。
//
// 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 KPROCESS_AUTO_ALIGNMENT_BIT 0
#define KPROCESS_DISABLE_BOOST_BIT 1
#define KPROCESS_DISABLE_QUANTUM_BIT 2
union {
struct { //x86未進行檢查
LONG AutoAlignment : 1; //用於該進程中的內存訪問對齊設置,此標誌位也會被傳遞到線程的數據結構中,當一個線程的對齊檢查開關打開時,該線程中的未對齊數據訪問將會導致對齊錯誤(alignment fault)
LONG DisableBoost : 1; //線程調度過程中的優先級提升
LONG DisableQuantum : 1; //線程調度過程中的時限(quantum)分配
LONG ReservedFlags : 29;
};
LONG ProcessFlags;
};
//KPROCESS中的BasePriority和QuantumReset域是該進程中的線程的調度參數,
SCHAR BasePriority; //指定一個進程中的線程的基本優先級,所有的線程在啓動時都會繼承進程的BasePriority值
SCHAR QuantumReset; //QuantumReset用於指定一個進程中線程的基本時限重置值,在現代Windows版本中此值被設置爲6
UCHAR State; //State域說明了一個進程是否在內存中,共有六種可能的狀態:ProcessInMemory、ProcessOutOfMemory、ProcessInTransition、ProcessOutTransition、ProcessInSwap和ProcessOutSwap。所謂一個進程在內存中,或者已被換出,或者正在轉移過程中,是指該進程的虛擬地址空間需要佔據足夠的物理內存,或者虛擬空間中的內容已被換出物理內存,或者正在換入或換出過程之中。
UCHAR ThreadSeed; //ThreadSeed域用於爲該進程的線程選擇適當的理想處理器(IdealProcessor),在每個線程被初始化的時候,都指定此進程的ThreadSeed值作爲它的理想處理器,然後ThreadSeed域又被設置一個新的值,以便該進程的下一個線程使用。這裏的理想處理器是指在多處理器環境下,每個線程都有一個優先選擇的處理器
UCHAR PowerState; //PowerState域用於記錄電源狀態,關於電源狀態管理
UCHAR IdealNode; //IdealNode域用於爲一個進程選擇優先的處理器節點,這是在進程初始化時設定的。這裏的處理器節點是NUMA(非一致的內存訪問)結構中的概念。
BOOLEAN Visited;
union {
KEXECUTE_OPTIONS Flags;
UCHAR ExecuteOptions; //用於設置一個進程的內存執行選項,這是爲了支持NX(No-Execute,內存不可執行)而引入到Windows XP/Server 2003中的一個域;
};
#if !defined(_X86_) && !defined(_AMD64_)
PALIGNMENT_EXCEPTION_TABLE AlignmentExceptionTable;
#endif
ULONG_PTR StackCount; //記錄了當前進程中有多少個線程的棧位於內存中
LIST_ENTRY ProcessListEntry; //用於將當前系統中所有具有活動線程的進程串成一個鏈表,鏈表頭爲KiProcessListHead
} KPROCESS, *PKPROCESS, *PRKPROCESS;
- Header域表明KPROCESS對象也是一個分發器對象(dispatcher object),進程對象本身是可以被等待的。
- ProfileListHead 域用於當該進程參與性能分析(profiling)時,作爲一個節點加入到全局的性能分析進程列表(內核全局變量KiProfileListHead)中。
- DirectoryTableBase是一個只有兩項的數組,其中第一項指向該進程的頁目錄表地址,第二項指向該進程的超空間(hyper space)的頁目錄表地址。
- 下來的LdtDescriptor、Int21Descriptor、IopmOffset和Iopl這四個域是專門針對Intel x86處理器的。其中LdtDescriptor 是該進程的LDT(局部描述符表)的描述符;Int21Descriptor是爲了兼容DOS程序,允許它們通過int 21h指令來調用DOS系統功能;IopmOffset則指定了IOPM(I/O權限表,I/O Privilege Map)的位置,內核通過IOPM可控制進程的用戶模式I/O訪問權限;Iopl域定義了進程的I/O優先級(I/O Privilege Level)。
- ActiveProcessors域記錄了當前進程正在哪些處理器上運行。
- KernelTime和UserTime域分別記錄了一個進程對象在內核模式和用戶模式下所花的時間。
- ReadyListHead是一個雙向鏈表的表頭,該鏈表記錄了這個進程中處於就緒狀態但尚未被加入全局就緒鏈表的線程。這個域的意義在於,當一個進程被換出內存以後,它所屬的線程一旦就緒,則被掛到此鏈表中,並要求換入該進程;以後,當該進程被換入內存時,ReadyListHead中的所有線程被加入到系統全局的就緒線程鏈表中。注意,ReadyListHead鏈表中的每一項都是一個指向KTHREAD對象(後文講述)的WaitListEntry域的地址,所以,從鏈表中的每一項都可以定位到對應的線程對象。
- SwapListEntry域是一個單鏈表項,當一個進程要被換出內存或者換入時,它通過此域加入到以KiProcessOutSwapListHead(出)/KiProcessInSwapListHead(入)爲鏈頭的單鏈表中。
- VdmTrapcHandler,指向處理Ctrl+C中斷的函數,僅用於在VDM(虛擬DOS機,Virtual DOS Machine)環境下運行16位程序。
- ThreadListHead域指向一個鏈表頭,此鏈表包含了該進程的所有當前線程,由此我們可以看出進程和線程之間的從屬關係。
- ProcessLock域,這是一個自旋鎖(spin lock)對象,它的用途是保護此進程中的數據成員。
- Affinity域指定了該進程的線程可以在哪些處理器上運行,其類型是KAFFINITY,這是一個32位或64位整數,其二進制表示的每一位分別對應於當前機器上的一個處理器(或核)。
- ProcessFlags域包括了進程中的幾個標誌:AutoAlignment、DisableBoost 和DisableQuantum。AutoAlignment位用於該進程中的內存訪問對齊設置,此標誌位也會被傳遞到線程的數據結構中,當一個線程的對齊檢查開關打開時,該線程中的未對齊數據訪問將會導致對齊錯誤(alignment fault),不過,Intel x86並未做任何對齊檢查,所以這一位的意義並不重要;DisableBoost和DisableQuantum位與線程調度過程中的優先級提升和時限(quantum)分配有關。
- KPROCESS中的BasePriority和QuantumReset域是該進程中的線程的調度參數,BasePriority用於指定一個進程中的線程的基本優先級,所有的線程在啓動時都會繼承進程的BasePriority值;QuantumReset用於指定一個進程中線程的基本時限重置值,在現代Windows版本中此值被設置爲6。
- State域說明了一個進程是否在內存中,共有六種可能的狀態:ProcessInMemory、ProcessOutOfMemory、ProcessInTransition、ProcessOutTransition、ProcessInSwap和ProcessOutSwap。所謂一個進程在內存中,或者已被換出,或者正在轉移過程中,是指該進程的虛擬地址空間需要佔據足夠的物理內存,或者虛擬空間中的內容已被換出物理內存,或者正在換入或換出過程之中。
- ThreadSeed域用於爲該進程的線程選擇適當的理想處理器(IdealProcessor),在每個線程被初始化的時候,都指定此進程的ThreadSeed值作爲它的理想處理器,然後ThreadSeed域又被設置一個新的值,以便該進程的下一個線程使用。這裏的理想處理器是指在多處理器環境下,每個線程都有一個優先選擇的處理器。
- PowerState域用於記錄電源狀態,關於電源狀態管理。
- IdealNode域用於爲一個進程選擇優先的處理器節點,這是在進程初始化時設定的。這裏的處理器節點是NUMA(非一致的內存訪問)結構中的概念。
- ExecuteOptions用於設置一個進程的內存執行選項,這是爲了支持NX(No-Execute,內存不可執行)而引入到Windows XP/Server 2003中的一個域。
- StackCount域記錄了當前進程中有多少個線程的棧位於內存中。
- ProcessListEntry域用於將當前系統中所有具有活動線程的進程串成一個鏈表,鏈表頭爲KiProcessListHead。
簡單歸納一下,KPROCESS對象中記錄的信息主要包括兩類:一類跟進程的內存環境相關,比如頁目錄表、交換狀態等;另一類是與其線程相關的一些屬性,比如線程列表以及線程所需要的優先級、時限設置等。系統中的KPROCESS對象通過KiProcessListHead鏈表串起來,但這一鏈表僅用於AMD64系統。系統的總進程鏈表是在執行體層管理的在EPROCESS對象結構裏。
KTHREAD定義(base\ntos\inc\ke.h中KTHREAD)
由於在Windows中,線程是系統處理器調度的基本單元,而且線程調度是在內核層完成的,所以,KTHREAD的許多域都跟Windows的線程調度機制有關。
typedef struct _KTHREAD {
//
// The dispatcher header and mutant listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header; //說明了內核層的線程對象也是一個分發器對象,線程可以被等待,當線程結束時,在該對象上的等待可被滿足
LIST_ENTRY MutantListHead; //M指向一個鏈表頭,該鏈表中包含了所有屬於該線程的突變體對象(mutant,對應於API中的互斥體[mutex]對象)。由於突變體對象是有所有權的,一旦被某個線程等到,則其所有權歸該線程所有,它也被連接到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.
//
//有四個域用於內核棧的維護,它們分別是:InitialStack、StackLimit、KernelStack和StackBase。棧是從高地址向低地址方向變化的
PVOID InitialStack; //記錄了原始的棧位置(高地址)
PVOID StackLimit; //記錄了棧的低地址
PVOID KernelStack; //記錄了真正內核調用棧的開始位置,由於在內核棧的頂部區域還記錄了浮點處理器保存區和一個異常陷阱幀,所以,KernelStack的位置比InitialStack要低一些
KSPIN_LOCK ThreadLock; //ThreadLock域是一個自旋鎖,用於保護線程數據成員。
union {
KAPC_STATE ApcState; //ApcState是一個結構成員,指定了一個線程的APC(Asynchronous Procedure Call)信息,包括APC鏈表、是否正在處理APC或者是否有內核APC或用戶APC正在等待等信息。
//KAPC_STATE中的Process域指向當前線程所屬進程的KPROCESS結構。
struct {
UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
BOOLEAN ApcQueueable; //是否可以插入APC
volatile UCHAR NextProcessor; //處理器調度的選擇
volatile UCHAR DeferredProcessor;
UCHAR AdjustReason; //優先級調整原因
SCHAR AdjustIncrement; //調整量
};
};
KSPIN_LOCK ApcQueueLock; //一個自旋鎖,用於保護APC隊列的操作
#if !defined(_AMD64_)
ULONG ContextSwitches;
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;
#endif
LONG_PTR WaitStatus; //記錄了等待的結果狀態。
union {
PKWAIT_BLOCK WaitBlockList; //指向一個以KWAIT_BLOCK爲元素的鏈表,其中的KWAIT_BLOCK對象指明瞭哪個線程在等待哪個分發器對象。
//對於一個線程而言,WaitBlockList域以及每個KWAIT_BLOCK對象中的WaitListEntry域構成了一個雙鏈表,指明瞭該線程正在等待哪些分發器對象;而對於每個分發器對象而言,它又有另一個KWAIT_BLOCK鏈表指明瞭哪些線程正在等待它。
PKGATE GateObject; //當一個線程正在等待門對象(Gate Object,也是一種分發器對象)時,GateObject域記錄了正在等待的門對象。由於等待門對象和等待其他分發器對象是不會同時發生的,所以,GateObject和WaitBlockList域構成了一個union,共用一個指針內存。
};
BOOLEAN Alertable; //說明了一個線程是否可以被喚醒,當一個線程正在等待時,如果它的Alertable值爲TRUE,則它可以被喚醒。
BOOLEAN WaitNext; //WaitNext域也是一個布爾值,TRUE值表示這個線程馬上要調用一個內核等待函數,它的用途是,在發出了一個信號(比如釋放了一個信號量對象)以後,接下來該線程會馬上調用等待函數,所以,它不必解除線程調度器鎖
UCHAR WaitReason; //記錄了一個線程的等待理由,其值的含義見base\ntos\inc\ke.h中的KWAIT_REASON枚舉類型
SCHAR Priority; //Priority域包含了該線程的優先級值,這是指它的動態優先級,即在執行過程中可能由於某些原因而調整過的優先級。
UCHAR EnableStackSwap; //EnableStackSwap域是一個布爾值,說明本線程的內核棧是否允許被換出到外存中。
volatile UCHAR SwapBusy; //指定了本線程當前是否正在進行上下文環境切換(context swap),其用法是,在將執行環境切換到其他線程以前設置SwapBusy域爲TRUE,切換完成以後再設置回FALSE。
BOOLEAN Alerted[MaximumMode]; //一個數組,指定了該線程在每一種警告模式下是否可以被喚醒,
//在WRK中,所謂警告模式實際上也就是內核模式和用戶模式,所以,這個數組的含義是指該線程分別在內核模式和用戶模式下是否可以被喚醒。
//WaitListEntry和 SwapListEntry分別是一個雙鏈表節點和一個單鏈表節點,它們是一個union,分別用於不同的情形。
union {
LIST_ENTRY WaitListEntry; //當一個線程正在等待被執行時,WaitListEntry作爲一個線程節點加入到某個鏈表中。
//例如,KPROCESS的ReadyListHead,在進程被換入內存過程中,就緒狀態的線程將被加入到以進程的 ReadyListHead域爲鏈表頭的雙鏈表中,鏈表中的節點即爲線程的WaitListEntry域。
SINGLE_LIST_ENTRY SwapListEntry;//用於當線程的內核棧需要被換入時,插入到以全局變量KiStackInSwapListHead爲鏈表頭的單鏈表中。時,插入到以全局變量KiStackInSwapListHead爲鏈表頭的單鏈表中。另外,當一個線程處於DeferredReady狀態時,其SwapListEntry將被插入到某個處理器的DeferredReadyListHead鏈表中
};
PRKQUEUE Queue; //一個隊列分發器對象,如果不爲NULL,則表示當前線程正在處理此隊列對象中的項。
#if !defined(_AMD64_)
ULONG WaitTime; //WaitTime域記錄了一個線程進入等待時刻的時間點(時鐘滴答值的低32位),主要用於平衡集管理器根據一個線程的等待時間的先後來做一些決策。
//接下來是KernelApcDisable和SpecialApcDisable域,或者合併成一個CombinedApcDisable域。
//KernelApcDisable和SpecialApcDisable都是16位的整數值,0表示不禁止APC,負數表示禁止APC,
//一個線程在執行過程中可以有多種因素要禁止APC,這些因素以負值來表示,並累加起來,當因素消除的時候再減去相應的負值。
//只有當KernelApcDisable或SpecialApcDisable爲0的時候,該線程才允許插入或提交APC。這兩個值分別控制普通的內核APC和特殊的內核APC
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
#endif
PVOID Teb; //指向進程地址空間中的一個TEB(線程環境塊)結構。
//TEB結構包含了在用戶地址空間中需要訪問的各種信息,例如與線程相關的GDI信息、系統支持的異常,甚至還有WinSock的信息,等等。
//關於TEB結構的定義,可以參考public\sdk\inc\pebteb.h中的_TEB定義。
//base\ntos\mm\procsup.c文件中的MmCreateTeb函數在一個進程中創建並初始化一個TEB。
union {
KTIMER Timer; //這是附在一個線程上的定時器,
//當一個線程在執行過程中需要定時器時,比如實現可超時的等待函數(KeWaitForSingleObject或KeWaitForMultipleObjects),就會用到此定時器對象。
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 {//AutoAlignment和DisableBoost兩個位標記,直接繼承自所屬進程的同名標記。
struct {
LONG AutoAlignment : 1;
LONG DisableBoost : 1;
LONG ReservedFlags : 30;
};
LONG ThreadFlags;
};
};
};
union {
KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1]; //WaitBlock域是一個包含4個KWAIT_BLOCK成員的數組,其中第4項專門用於可等待的定時器對象。
//KWAIT_BLOCK結構代表了一個線程正在等待一個分發器對象,或者說一個分發器對象正在被一個線程等待,它會被同時加入到兩個雙鏈表結構中。
//WaitBlock域是一個內置數組,內核在實現等待功能的時候,如果一個線程所等待的對象數量小於4(實際上應該是3個分發器對象加上一個定時器對象),
//則內核無須另外分配KWAIT_BLOCK對象內存,只需直接使用WaitBlock中的數組成員即可。如果等待的對象數量大於4,
//則內核必須分配額外的KWAIT_BLOCK對象內存。
//由於等待操作在內核中非常頻繁,所以,利用靜態數組來滿足大多數情況下的內存需求,這一優化非常有意義。
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; //WaitIrql域與WaitNext一起使用,當WaitNext爲TRUE時,WaitIrql記錄了原先的IRQL值。
KPROCESSOR_MODE WaitMode; //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
};
LIST_ENTRY QueueListEntry; //記錄了線程在處理一個隊列項時加入到隊列對象的線程鏈表中的節點地址。
//
// The following fields are accessed during system service dispatch.
//
PKTRAP_FRAME TrapFrame; //記錄控制流狀態的數據結構,它是一個指向KTRAP_FRAME類型的指針。
//在Windows中,線程是系統調度的基礎,它代表了一個進程中的一個控制流。
//當一個線程離開運行狀態時,其當前的執行狀態,比如現在的指令指針(IP)在哪裏,各個寄存器中的值是什麼,等等,
//都必須保存下來,以便下次再輪到這個線程運行時,可以恢復原來的執行狀態。
//對於Intel x86體系結構,KTRAP_FRAME的定義位於base\ntos\inc\i386.h文件中,它包含了Intel x86的所有常用寄存器
PVOID CallbackStack; //包含了線程的回調(callback)棧地址,此棧在該線程從內核模式調用到用戶模式時使用。
PVOID ServiceTable; //ServiceTable域指向該線程使用的系統服務表(全局變量KeServiceDescriptorTable),
//如果這是一個圖形用戶界面(GUI)線程,此域將指向另一個影子系統服務表(全局變量KeServiceDescriptorTableShadow),
#if defined(_AMD64_)
ULONG KernelLimit;
#endif
//
// The following fields are referenced during ready thread and wait
// completion.
//
UCHAR ApcStateIndex; //ApcStateIndex域是一個索引值,它指明瞭當前的APC狀態在ApcStatePointer域中的索引。
//由於ApcStatePointer是一個只有兩個元素的數組,所以,ApcStateIndex的值爲0或者1
UCHAR IdealProcessor; //指明瞭在多處理器的機器上該線程的理想處理器。
BOOLEAN Preempted; //Preempted域是一個布爾值,說明這個線程是否被高優先級的線程搶佔了,
//只有當一個線程正在運行或者正在等待運行而被高優先級線程搶佔的時候,此值纔會置成TRUE。在其他情況下,該值總是FALSE。
BOOLEAN ProcessReadyQueue; //說明一個線程是否在所屬進程KPROCESS對象的ReadyListHead鏈表中,TRUE表示在此鏈表中,FALSE表示不在。
#if defined(_AMD64_)
PVOID Win32kTable;
ULONG Win32kLimit;
#endif
BOOLEAN KernelStackResident; //說明該線程的內核棧是否駐留在內存中,當內核棧被換出內存時,該值將被置成FALSE;當換入內存時,再置成TRUE。
SCHAR BasePriority; //線程的靜態優先級,其初始值是所屬進程的BasePriority值,以後可通過KeSetBasePriorityThread函數重新設定。
SCHAR PriorityDecrement; //記錄了一個線程在優先級動態調整過程中的遞減值,每次當重新計算優先級值的時候,它會被減掉(參見base\ntos\ke\ki.h中的KiComputeNewPriority函數)。
//Windows中的優先級值被分成兩個區域:0~15是普通線程的優先級,16~31是實時線程的優先級。而且,線程的優先級在調整時,無論如何其Priority值不會跨越區域。
CHAR Saturation; //Saturation域說明了線程的基本優先級相對於進程的基本優先級的調整量是否超過了整個區間的一半,其值爲0、1或-1。
KAFFINITY UserAffinity; //線程的用戶親和性,此值初始時也繼承自進程對象的Affinity值,以後可通過內核函數KeSetAffinityThread改變。
PKPROCESS Process; //指向線程的進程對象,在線程初始化時指定,見base\ntos\ke\thredobj.c中的KeInitThread函數。
KAFFINITY Affinity; //Affinity域指定了線程的處理器親和性,此值初始時繼承自進程對象的Affinity值。爲線程指定的處理器集合必須是其進程的親和性處理器集合的子集。
//在線程執行過程中,其Affinity值可能有兩種設置:
//一是系統親和性,當該線程執行系統任務時通過KeSetSystemAffinityThread函數來設置;
//二是線程本身的親和性,稱爲用戶親和性,通過KeRevertToUserAffinityThread函數來設置。
//
// The below fields are infrequently referenced.
//
PKAPC_STATE ApcStatePointer[2]; //ApcStatePointer數組元素的類型是指向KAPC_STATE的指針,其兩個元素分別指向線程對象的ApcState和SavedApcState域,而這兩個域分別位於兩個union中。
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
};
};
PVOID Win32Thread; //一個指針,指向由Windows子系統管理的區域。
PVOID StackBase; //記錄了當前棧的基位置(高地址)。
//SuspendApc和SuspendSemaphore兩個union域相互之間有聯繫,
//SuspendApc被初始化成一個專門的APC。當該APC被插入並交付時,KiSuspendThread函數被執行,
//其執行結果是在線程的SuspendSemaphore信號量上等待,直到該信號量對象有信號,然後線程被喚醒並繼續執行。
//線程的掛起(suspend)操作正是通過這一機制來實現的,參見base\ntos\ke\thredobj.c中的KeSuspendThread和KeFreezeAllThreads函數。
//自然地,線程的恢復(resume)操作則是通過控制SuspendSemaphore信號量的計數來實現的,參見base\ntos\ke\thredobj.c中的KeResumeThread等函數。
union {
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 {
KSEMAPHORE SuspendSemaphore;
struct {
UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
ULONG SListFaultCount;//它記錄了在地址SListFaultAddress上發生頁面錯誤的次數。
};
};
LIST_ENTRY ThreadListEntry;//代表了一個雙鏈表上的節點,當一個線程被創建時,它會被加入到進程對象的ThreadListHead鏈表中,參見KPROCESS的ThreadListHead域的介紹。
PVOID SListFaultAddress; //SListFaultAddress域與用戶模式互鎖單鏈表POP操作(KeUserPopEntrySListFault函數)的錯誤處理有關,
//它記錄了上一次用戶模式互鎖單鏈表POP操作發生頁面錯誤的地址;
#if defined(_WIN64)
LONG64 ReadOperationCount;
LONG64 WriteOperationCount;
LONG64 OtherOperationCount;
LONG64 ReadTransferCount;
LONG64 WriteTransferCount;
LONG64 OtherTransferCount;
#endif
} KTHREAD, *PKTHREAD, *PRKTHREAD;
- Header說明了內核層的線程對象也是一個分發器對象,線程可以被等待,當線程結束時,在該對象上的等待可被滿足。
- MutantListHead域指向一個鏈表頭,該鏈表中包含了所有屬於該線程的突變體對象(mutant,對應於API中的互斥體[mutex]對象)。由於突變體對象是有所有權的,一旦被某個線程等到,則其所有權歸該線程所有,它也被連接到MutantListHead鏈表中。
- 有四個域用於內核棧的維護,它們分別是:InitialStack、StackLimit、KernelStack和StackBase。棧是從高地址向低地址方向變化的,InitialStack記錄了原始的棧位置(高地址);StackLimit記錄了棧的低地址;KernelStack記錄了真正內核調用棧的開始位置,由於在內核棧的頂部區域還記錄了浮點處理器保存區和一個異常陷阱幀,所以,KernelStack的位置比InitialStack要低一些(KTRAP_FRAME_LENGTH+sizeof(FX_SAVE_AREA))。StackBase記錄了當前棧的基位置(高地址)。在線程初始化時,InitialStack和StackBase是相等的,都指向原始的內核棧高地址。
- ThreadLock域是一個自旋鎖,用於保護線程數據成員。ApcState是一個結構成員,指定了一個線程的APC(Asynchronous Procedure Call)信息,包括APC鏈表、是否正在處理APC或者是否有內核APC或用戶APC正在等待等信息。KAPC_STATE中的Process域指向當前線程所屬進程的KPROCESS結構。在ApcState所在的union中,ApcState之後還有幾個域:ApcQueueable、NextProcessor、DeferredProcessor、AdjustReason和AdjustIncrement,分別指定了以下設置:是否可以插入APC、關於處理器調度的選擇,以及優先級調整原因和調整量。ApcQueueLock也是一個自旋鎖,用於保護APC隊列的操作。
- Alertable域說明了一個線程是否可以被喚醒,當一個線程正在等待時,如果它的Alertable值爲TRUE,則它可以被喚醒。
- WaitNext域也是一個布爾值,TRUE值表示這個線程馬上要調用一個內核等待函數,它的用途是,在發出了一個信號(比如釋放了一個信號量對象)以後,接下來該線程會馬上調用等待函數,所以,它不必解除線程調度器鎖。
- WaitIrql域與WaitNext一起使用,當WaitNext爲TRUE時,WaitIrql記錄了原先的IRQL值。WaitReason域記錄了一個線程的等待理由,其值的含義見base\ntos\inc\ke.h中的KWAIT_REASON枚舉類型。
- WaitReason基本上只是記錄了等待的理由,而並不參與到線程調度或決策中。
- WaitMode域記錄了當線程等待時的處理器模式,即內核模式或用戶模式的等待。
- WaitStatus域,它記錄了等待的結果狀態。
- WaitBlockList成員指向一個以KWAIT_BLOCK爲元素的鏈表,其中的KWAIT_BLOCK對象指明瞭哪個線程在等待哪個分發器對象。對於一個線程而言,WaitBlockList域以及每個KWAIT_BLOCK對象中的WaitListEntry域構成了一個雙鏈表,指明瞭該線程正在等待哪些分發器對象;而對於每個分發器對象而言,它又有另一個KWAIT_BLOCK鏈表指明瞭哪些線程正在等待它.
- Priority域包含了該線程的優先級值,這是指它的動態優先級,即在執行過程中可能由於某些原因而調整過的優先級。BasePriority域是線程的靜態優先級,其初始值是所屬進程的BasePriority值,以後可通過KeSetBasePriorityThread函數重新設定。PriorityDecrement域記錄了一個線程在優先級動態調整過程中的遞減值,每次當重新計算優先級值的時候,它會被減掉。Windows中的優先級值被分成兩個區域:0~15是普通線程的優先級,16~31是實時線程的優先級。而且,線程的優先級在調整時,無論如何其Priority值不會跨越區域。Saturation域說明了線程的基本優先級相對於進程的基本優先級的調整量是否超過了整個區間的一半,其值爲0、1或-1。
- EnableStackSwap域是一個布爾值,說明本線程的內核棧是否允許被換出到外存中。
- SwapBusy域也是一個布爾值,指定了本線程當前是否正在進行上下文環境切換(context swap),其用法是,在將執行環境切換到其他線程以前設置SwapBusy域爲TRUE,切換完成以後再設置回FALSE。
- Alerted域是一個數組,指定了該線程在每一種警告模式下是否可以被喚醒,在WRK中,所謂警告模式實際上也就是內核模式和用戶模式,所以,這個數組的含義是指該線程分別在內核模式和用戶模式下是否可以被喚醒。
- WaitListEntry和 SwapListEntry分別是一個雙鏈表節點和一個單鏈表節點,它們是一個union,分別用於不同的情形。當一個線程正在等待被執行時,WaitListEntry作爲一個線程節點加入到某個鏈表中。例如,本節前文在介紹KPROCESS的ReadyListHead域時曾提到,在進程被換入內存過程中,就緒狀態的線程將被加入到以進程的 ReadyListHead域爲鏈表頭的雙鏈表中,鏈表中的節點即爲線程的WaitListEntry域。
- SwapListEntry域則被用於當線程的內核棧需要被換入時,插入到以全局變量KiStackInSwapListHead爲鏈表頭的單鏈表中。另外,當一個線程處於DeferredReady狀態時,其SwapListEntry將被插入到某個處理器的DeferredReadyListHead鏈表中。
- Queue域是一個隊列分發器對象,如果不爲NULL,則表示當前線程正在處理此隊列對象中的項。
- WaitTime域記錄了一個線程進入等待時刻的時間點(時鐘滴答值的低32位),主要用於平衡集管理器,根據一個線程的等待時間的先後來做一些決策。
- 接下來是KernelApcDisable和SpecialApcDisable域,或者合併成一個CombinedApcDisable域。KernelApcDisable和SpecialApcDisable都是16位的整數值,0表示不禁止APC,負數表示禁止APC,一個線程在執行過程中可以有多種因素要禁止APC,這些因素以負值來表示,並累加起來,當因素消除的時候再減去相應的負值。只有當KernelApcDisable或SpecialApcDisable爲0的時候,該線程才允許插入或提交APC。這兩個值分別控制普通的內核APC和特殊的內核APC。
- Teb域是一個特殊的域,它指向進程地址空間中的一個TEB(線程環境塊)結構。TEB結構包含了在用戶地址空間中需要訪問的各種信息,例如與線程相關的GDI信息、系統支持的異常,甚至還有WinSock的信息,等等。
- 這是附在一個線程上的定時器,當一個線程在執行過程中需要定時器時,比如實現可超時的等待函數(KeWaitForSingleObject或KeWaitForMultipleObjects),就會用到此定時器對象。
- WaitBlock域是一個包含4個KWAIT_BLOCK成員的數組,其中第4項專門用於可等待的定時器對象。KWAIT_BLOCK結構代表了一個線程正在等待一個分發器對象,或者說一個分發器對象正在被一個線程等待,它會被同時加入到兩個雙鏈表結構中。
- 記錄了線程在處理一個隊列項時加入到隊列對象的線程鏈表中的節點地址。
- 記錄控制流狀態的數據結構,它是一個指向KTRAP_FRAME類型的指針。
- CallbackStack域包含了線程的回調(callback)棧地址,此棧在該線程從內核模式調用到用戶模式時使用。
- ServiceTable域指向該線程使用的系統服務表(全局變量KeServiceDescriptorTable),如果這是一個圖形用戶界面(GUI)線程,此域將指向另一個影子系統服務表(全局變量KeServiceDescriptorTableShadow)。
- IdealProcessor域指明瞭在多處理器的機器上該線程的理想處理器。
- Preempted域是一個布爾值,說明這個線程是否被高優先級的線程搶佔了,只有當一個線程正在運行或者正在等待運行而被高優先級線程搶佔的時候,此值纔會置成TRUE。在其他情況下,該值總是FALSE。
- ProcessReadyQueue域也是一個布爾值,說明一個線程是否在所屬進程KPROCESS對象的ReadyListHead鏈表中,TRUE表示在此鏈表中,FALSE表示不在。
- KernelStackResident域也是一個布爾值,說明該線程的內核棧是否駐留在內存中,當內核棧被換出內存時,該值將被置成FALSE;當換入內存時,再置成TRUE。
- Affinity域指定了線程的處理器親和性,此值初始時繼承自進程對象的Affinity值。爲線程指定的處理器集合必須是其進程的親和性處理器集合的子集。
- UserAffinity域是線程的用戶親和性,此值初始時也繼承自進程對象的Affinity值,以後可通過內核函數KeSetAffinityThread改變。
- Process域指向線程的進程對象,在線程初始化時指定。
- ApcStateIndex域是一個索引值,它指明瞭當前的APC狀態在ApcStatePointer域中的索引。
- ApcStatePointer數組元素的類型是指向KAPC_STATE的指針,其兩個元素分別指向線程對象的ApcState和SavedApcState域,而這兩個域分別位於兩個union中。
- 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等函數。
- ThreadListEntry域代表了一個雙鏈表上的節點,當一個線程被創建時,它會被加入到進程對象的ThreadListHead鏈表中,參見本節前文關於KPROCESS的ThreadListHead域的介紹。
- SListFaultAddress域與用戶模式互鎖單鏈表POP操作(KeUserPopEntrySListFault函數)的錯誤處理有關,它記錄了上一次用戶模式互鎖單鏈表POP操作發生頁面錯誤的地址;
從以上介紹的KPROCESS和KTHREAD數據結構我們可以看出,內核層的進程和線程對象只包含了系統資源管理和多控制流併發執行所涉及的基本信息,而沒有包含與應用程序相關聯的信息,如進程映像文件和線程啓動函數地址等。由於Windows的線程調度算法比較複雜(在搶佔式調度算法的基礎上,還支持優先級局部調整等),且需要支持某些硬件結構特性,所以,在KPROCESS和KTHREAD結構中,有些成員的引入直接跟這些特性有關。儘管如此,從概念上講,內核層上的進程和線程對象仍然十分清晰,即,進程對象提供了線程的基本執行環境,包括進程地址空間和一組進程範圍內公用的參數;線程對象提供了爲參與線程調度而必需的各種信息及其維護控制流的狀態。