1 Chapter3 Windows 行程和緒程 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒層物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 3.4.2 獲得目前的行程和緒程 2 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 3 3.4.2 獲得目前的行程和緒程 • 行程 (Process) – 定義了以位址空間為基本的執行環境 – 電腦中已執行程式的實體 – 本身不會執行,是緒程 (Thread) 的容器 4 • 現代作業系統提供多個工作能夠並行執行 的環境 • 偽平行 – 單核心處理器 – 將時間細分並輪流執行不同的工作 • 真平行 – 多核心處理器 – 多個工作分別在不同的處理器上執行 5 • 維護一個全域的行程表 – 記錄下目前有哪些行程正在被執行 • 把時間分成適當的片段 – 設置時鐘中斷來完成 • 在行程之間實施切換 – 保留 一個行程的環境資訊,恢復下一個行程的 執行環境 6 • 程式 – 指令的集合 – 為了完成特定的功能或工作而存在 – 需要有程式碼、暫存器、記憶體…等來配合 • 程式本身只是指令的集合,行程才是程式 的真正執行 7 8 • 靜態資料區 – 全域變數、靜態變數 – 編寫程式碼時就確定的資料區域 • 動態資料區 – 程式執行時依需要而分配或回收的記憶體區域 • 堆疊 – 紀錄執行過程中的函式狀態資訊 • 呼叫位置、區域變數…等 – 傳遞函式參數 9 • 行程的建立 – 作業系統啟動時 – 程式所需要時 • 行程的結束 – 行程完成工作之後 – 行程出錯或是狀態不正確時 10 • 狀態:行程目前的動作 – 行程執行時,狀態 (state) 會改變 狀態 描述 新生 (new) 行程新產生中。 執行 (running) 正在執行。 等待 (waiting) 等待某事發生,例如等待使用者輸入完成。 就緒 (ready) 排班中,等待CPU執行權。 結束 (terminated) 完成執行。 11 12 項目 描述 行程狀態 (Process State) 可以是 new、ready、running、waiting 或 halted 等 行程識別號 (Process Identifier) Process 唯一的識別號,也就是 PID • 行程控制表(Process Control Block, PCB) 程式計數器 接下來要執行的指令位址 – 是作業系統核心中一種資料結構,主要表示行 (Program Counter) 程狀態 CPU暫存器 (CPU Registers) 紀錄CPU暫存器的值,主要用途在於中斷時暫時 儲存資料,以便稍後繼續利用;其數量及類別因 電腦架構有所差異。 CPU排程資訊 包括行程的優先順序 (Priorlty)、排班佇列 (CPU Scheduling Information) (Scheduling queue) 的指標以及其它的排程參數 記憶體管理 (Memory-management Information) 基底暫存器(base register) 和限制暫存器(limit register),分頁表(Page table)值的資訊所使用 的記憶系統區段表(segment table)。 會計資訊 (Accounting Information) 如CPU與實際時間之使用數量、時限、帳號、工 作或行程號碼。 輸出輸入狀態 (I/O Status Information) 配置給行程的輸入/輸出裝置,包括開啟檔案的串 列 等等 13 14 • 緒程 (Thread) – 一個指令序列,可直接存取所屬行程中的資源 – 是作業系統能夠進行運算調度的最小單位 – 每個行程至少有一個緒程 – 每個緒程每一個時刻必屬於某一個行程 – 有時候也被稱為輕量行程 (Lightweight Process) 15 • 緒程 (Thread) 組成包含以下元素: – 程式計數器 – CPU暫存器 • 目前 CPU 的狀態 – 堆疊空間 (Stack) • 紀錄函式呼叫路徑以及函式所用到的變數等 – 排程優先權、已用掉的 CPU time • 以便系統對不同的緒程做排程 16 • 一個行程 (Process) 中,可有多個緒程 (Thread) • 同個行程中的緒程之間彼此共享 – 行程的狀態及資源 – 相同的記憶體空間 (Memory Space),存取相同 的資料 • User-level thread • Kernel-level thread 17 • 應用程式自行管理 Thread – 核心不會察覺 Thread 的存在 – 任何應用程式可使用 Thread 函式庫 (library),達到 Multi-Thread – 由 Thread Library 來建立與 管理 (User mode) • Thread 函式庫包括: – 建立及刪除 Thread – 在 Thread 間送訊息及資料 – Thread 排程 – … etc 18 • 優點 – – – – 切換效率高 可根據應用程式的特性進行排程 可攜性高 Overhead 較低 • 缺點 – 當 Thread 執行有懸置性質 (blocking) 的系統呼叫 時,除了此 Thread 被懸置外,其餘 Thread 也會 遭懸置 – 在多處理器的環境下,User-Level Thread 只能在 一個 CPU 上執行 19 • 核心管理 Thread – 提供 Thread 相關應用 程式介面(API) – 由核心來建立及管理 20 • 優點 – Kernel-Level Thread 可在不同的 CPU 上平行 執行 – 當 Process 中的一個 Thread 被懸置,其他未 被懸置的 Thread 仍可執行 • 缺點 – Kernel-Level Threads 較沒效率 – Overhead 較高 – 可攜性低 21 • 如果作業系統支援 Kernel-Level Thread, 則排程通常在 Thread 的粒度上執行 • 衡量準則 – 公平性 – CPU 的有效利用 22 • FIFO (先進先出) – 所有 Thread 構成一個 FIFO 的佇列 – 先到的先獲得執行權,直到執行結束後釋出 – 概念簡單,易於實作 – 演算法的實際效果可能非常不公平 • 若每個 Thread 所需的工作時間長短不一 23 • Round-Robin (時段輪轉) – CPU 處理時間被切割成時間片段,稱為時段 – 以輪轉方式分配時段給每一個 Thread – Thread 執行直到時段用完或是主動放棄執行權 – 概念簡單、易於實作 – 使用廣泛,也確實能公平分配處理器資源 24 • Priority-Based (優先層級) – 每個 Thread 都有一個優先權值 (Priority) – 高優先權的 Thread 優先被執行 – 相同優先權的 Thread 則使用 FIFO 或 RR – 高優先權的 Thread 可能會霸佔 CPU 導致低優 先權的 Thread 沒有機會執行 – 導入了動態優先權的概念 • 連續執行多個時段的 Thread 要降低優先權 • 長時間沒有得到執行權的 Thread 提升優先權…等 25 26 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 27 3.4.2 獲得目前的行程和緒程 28 PEB TEB EPROCESS KPROCESS _HANDLE_TABLE ETHREAD KTHREAD 29 Any Handle Table Object Manager Process Object Thread Thread Files Events Devices Drivers Process’ Handle Table Virtual Address Descriptors Thread Thread Thread Thread 30 CreateProcess() Locate imagefile (path search) Convert DOS name to NT name Call NtOpenFile() Call NtCreateSection(SEC_IMAGE) Check for special handling: VDM, WoW64, restrictions, CMD files Call NtQuerySection() to get ImageInformation Use LdrQueryImageFileExecutionOptions() to see if debugging Special handling for POSIX executable Create the new process in the kernel via NtCreateProcessEx() If requested, call NtSetInformationProcess(ProcessPriorityClass) If (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) call NtSetInformationProcess(ProcessDefaultHardErrorMode) 31 Call BasePushProcessParameters() to push params into new process Stuff in the standard handles if needed Call BaseCreateStack() to create a user-mode stack in process Call BaseInitializeContext() to create an initial thread context Call NtCreateThread() to create the first thread // thread may run, so no more modification to new process virtual space Use CsrClientCallServer(BasepCreateProcess) to register new process and thread with CSRSS If app is restricted Set a restricted token on the process assign it to a job object so that it can't escape the token. Unless the initial thread was created suspended, start it with NtResumeThread() 32 Header PowerState SwapListEntry VdmTrapcHandler ProfileListHead IdealNode DirectoryTableBase Visited ThreadListHead LdtDescriptor StackCount ProcessLock Int21Descriptor ProcessListEntry Affinity IopmOffset ProcessFlags Iopl BasePriority ActiveProcessors QuantumReset KernelTime State UserTime ReadyListHead ThreadSeed #include <stdlib.h> union { KernelTime ULONG Busy-waiting SINGLE_LIST_ENTRY SwapListEntry DISPATCHER_HEADER Header UCHAR State ULONG_PTR DirectoryTableBase[2] ReadyListHead LIST_ENTRY ProcessListEntry StackCount BOOLEAN Visited KIDTENTRY Int21Descriptor #include <stdio.h> PVOID VdmTrapcHandler Iopl struct { ProfileListHead KAFFINITY SCHAR UCHAR BasePriority PowerState Affinity USHORT volatile KSPIN_LOCK KAFFINITY IopmOffset ProcessLock ActiveProcessors ThreadListHead IdealNode KGDTENTRY LdtDescriptor Spin Lock QuantumReset UCHAR ThreadSeed ULONG UserTime struct Employee{ LONG AutoAlignment : 1; 記錄一個行程物件在Kernel mode和User mode •1. Singly linked list structure。 兩個元素的陣列。 1. KPROCESS物件是一個dispatcher object。 說明Process是否在記憶體中 int ID; LONG DisableBoost : 1; Doubly linked list structure。 允許它們透過int 21h指令來呼叫DOS系統功能 定義了Process 指向處理Ctrl+C中斷的函式,僅用於VDM I/O Privelege Level。 指定了該Process的thread可以在哪些處理器上 Thread的schedule參數。 記錄電源狀態,關於電源狀態管理(chap 該行程參與效能分析時,加入到全域的效能分 6.4)。 •1. 紀錄了目前Process中有多少thread的堆 將目前系統中所有具有活動Thread之 指定IOPM(I/O 主要是紀錄了目前行程正在哪些Processor上執 Thread的schedule參數。 Spinlock。 Privilege Map,I/O權限表)。 WRK中未使用。 包含了該Process所有current thread。 一個Process優先選擇的處理器節點(NUMA結構 Process的LDT(local descriptor table)描述項。 利用busy-waiting的技巧 ••2. 為該行程的Thread選擇適當的Ideal Process運用loop敍述執行來達到強迫暫停/等 下所花的時間。 Memory 第一個指向該行程的分頁目錄表位址。 2. Dispatcher object的定義及用法,參考Chap • char ProcessInMemory name[7]; LONG DisableQuantum : 1; 紀錄了這個Process中處於就緒但尚未加入全域 (為了相容DOS)。 (Virtral Dos Machine)的環境下執行16位元 析行程清單(核心全域變數)中作為一個節點。 •2. 執行。 Process中的thread的基本優先層級。 行。 用來保護這個Process中的資料成員。 指定Process中thread基本配量重置值。 當Thread被建立時,便會加到此;終止的時候 概念) 主要是與segment的記憶體管理有關。 疊放至於記憶體中。 process串成一個串列 Processor(在Multiprocessor)。 待之效果。 Swap out •3. Process的KernelTime和UserTime時間值等於其 Wait(s): 第二個指向該行程的hyper 5.4。 •1.int ProcessOutOfMemory salary; LONG ReservedFlags : space的分頁目錄 29; I/O存取權限。 每個KPROCESS物件代表了一個行 程式。 所有Thread在啟動時都會繼承這個值。 •3. Kernel透過IOPM控制User 確保Process資料結構的修改和存取是一致的。 在windows為6(chap 3.5會說明為什麼是6)。 mode的 從list中移除。 內容主要是與segment的起始位置,有效範圍 透過此欄位加入到KiProcessOutSwapListHead。 串列開頭為KIProcessListHead •3. 就緒list的Thread。 每個Thread初始化時,都會拿這個的值作為它 所屬的Thread KernelTime和UserTime的值之和。 whileswap s <= 0 do no-op; 表位址。 當行程物件變成有信號狀態時(這發生在行程 •};While ProcessInTransition };程,反之亦然 當Process out記憶體後,所屬的Thread 等相關屬性。 Swap in (condition) do no-op; 的Ideal Processor。 •4. s = s-1; 系统一般只使用DirectoryTableBase[0]。 結束時)此等待就會成功,讓thread or •4. 當Thread結束才會更新其Process的這兩個時間 ProcessOutTransition LONG ProcessFlags; 透過此欄位加入到 如果就緒,它就會被掛在這裡,並要求swap in 我們可在第4章中看到他分成GDT和LDT。 Threadseed會設置一個新值,以便給該 •Signal(s): 若條件式持續為True則Process會在此while 值。 2.};Process。 KPROCESS在EPROCESS建立起來 5. 我們只需要拿到目標行程裡的 KiProcessInSwapListHead(link file)。 process可以達到等待的動作。 •int ProcessInSwap main(){ Process的下一個Thread使用。 loop打轉,無法離開,形成waiting效果,直 •• typedef 只要Process尚未有任一Thread結束,則這兩個 sEmployee =struct s+1; _KPROCESS { DirectoryTableBase[0]值並直接設定到CR3中 ProcessOutSwap struct emp; 時建立 當Process進入到記憶體後,它會在將這串列上 到條件式為False,Process才離開while往下執 值是會為0。 就可以讀取了。 printf("size of *PKPROCESS,*PRKPROCESS; Employee is %d\n", list。 •ProcessFlags: 缺點:waiting process仍需同其它process爭 } 的東西全部加入到全域就緒Thread KPROCESS, 3. KPROCESS的位址和EPROCESS的 行。 6. 行程的分頁目錄表和hyper space參考chap 4。 sizeof(emp)); •1. 下面的code則是一個利用到ReadyListHead的例 奪cpu,將搶來的cpu time用在無實質進展的 AutoAlignment 位址是相同的 system("pause"); 子,它主要的工作就是,將所有的Thread加入 loop測試上,只是為了達到強迫等待之效果, • 用於Process存取記憶體對齊的設置。 return -1; 全域就緒Thread list中。 此舉是浪費cpu time。 • 也會被傳送到Thread的資料結構中,當 Process->State=ProcessInMemory; •} 優點:節省不必要的context switching,若 Thread得對齊檢查打開後,如果沒有對齊 NextEntry=Process->ReadyListHead.Flink; process可以在極短的時間內,離開loop往下執 就會產生對齊錯誤。 While(NextEntry != &Process->ReadyListHead){ Thread=CONTAIN_RECORD(NextEntry, KTHREAD, WaitListEntry); 行,不被卡住,則spinlock是有利的。 • Intel x86中並沒有作對齊檢查 RemoveEntryList(NextEntry); Thread->ProcessReadyQueue=FALSE; 2. DisableBoost、DisableQuantum KGDTENTRY Intel x86 “區段+偏移” KiReadyThread(Thread);//加入global ready thread list function • 資料結構 這兩個旗標則是與Thread中的優先層級提 形式的邏輯位址解析過程 NextEntry=Process->ReadyListHead.Flink; } 升與配量分配有關,(會在Chap 3.5中有 解說) 33 34 Header WaitNext MutantListHead WaitReason KernelNpxState stack maintain ThreadLock WaitIrql ApcState WaitMode ApcState所在union WaitStatus 之後的欄位 ApcQueueLock WaitBlockList ContextSwitche GateObject State Alertable PVOID InitialStack DISPATCHER_HEADER Header typedef struct _KAPC_STATE { union { volatile UCHAR State union { BOOLEAN Alertable ThreadLock KSPIN_LOCK ULONG UCHAR WaitReason ContextSwitches ApcQueueLock KPROCESSOR_MODE LONG_PTR WaitStatus WaitMode KIRQL WaitIrql NpxState BOOLEAN WaitNext LIST_ENTRY MutantListHead PVOID StackLimit LIST_ENTRY ApcListHead[MaximumMode]; PKWAIT_BLOCK WaitBlockList; KAPC_STATE ApcState; KernelStack struct _KPROCESS *Process; PKGATE GateObject; ••PVOID Kernel層的Thread object也是Dispatcher struct { 反應該Thread目前的狀態 Thread是否可以被喚醒。 Spin lock。 記錄了該Thread進行多少次context switch。 Spin 記錄Thread為什麼要等待的理由。 Lock。 記錄Thread等待時的Processor 記錄等待的結果狀態。 mode 記錄了原先的IRQL的值。 反映了浮點處理器的狀態。 •PVOID True:表示這個Thread馬上要呼叫一個Kernel 指向list head。 StackBase BOOLEAN KernelApcInProgress; };object。 True:喚醒。 用於保護Thread資料成員。 保護APC Queue的操作。 •• 不參與Thread排程或決策中。 UCHAR Kernel Mode 等待函式。 包含所有屬於該Thread的mutant BOOLEAN KernelApcPending; •1.1. Thread可以被等待。 ApcStateFill[KAPC_STATE_ACTUAL_LENGTH]; InitialStack Windows User Mode 中的 thread 是系統處理 •• BOOLEAN 發出一個Signal後,Thread馬上會叫呼叫等待 一旦某個Thread等到了一個mutant則所 記錄了正在被等待的Gate Object。 UserApcPending; • 指向KWAIT_BLOCK為元素的串列。 當Thread結束時,該物件上的等待可被滿足。 BOOLEAN ApcQueueable; • 記錄原始Stack位置(高位址)。 函式,所以不用解除Thread排程器鎖。 器排程的基本單元,而且是在 有權歸讓Thread所有,它會被連接到 }•2.KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE; 等待Gate Object和等待其它Dispatcher 每個KWAIT_BLOCK物件指明哪個Thread在 volatile UCHAR NextProcessor; StackLimit MutantListHead。 level 完成的,所以 object是不會同時發生。 volatile UCHAR DeferredProcessor; 等待哪個Dispatcher object。 •Kernel 記錄Stack的低位址。 struct _KTHREAD { Include APC list。 UCHAR AdjustReason; •1. GateObject和WaitBlockList構成一個 3. typedef KernelStack KTHREAD很多欄位與Windows的 每個KWAIT_BLOCK物件中的WaitListEntry SCHAR AdjustIncrement; 2.} union,共用一個指標記憶體。 是否正在處理 APC。 •thread排班機制有關。 記錄了真正Kernel呼叫stack的開始位置。 指明該Thread正在等待哪些Dispatcher KTHREAD, *PKTHREAD,*PRKTHREAD; };• 記錄浮點處理器保存區和一個 3. 是否有Kernel APC或是否有User APC正 object;而每個Dispatcher object,它 2. 有些欄位的涵義需結合Windows的 Typedf struct _KGATE{ };在等待 EXCEPTION TRAP FRAME。 又有另一個KWAIT_BLOCK LIST指明了哪些 DISPATCHER_HEADER Header; • KernelStack的位置比InitialStack要低一 thread排班機制來了解。 4. Process 欄位指向目前Thread所屬 ••}KGATE,*PKGATE; ApcQueueable Thread正在等待它(Chap 5.4)。 ApcState是一個Struct ,指定了一個 些 Process的KPROCESS 是否可插入APC。 結構。 KTRAP_FRAME_LENGTH + thread的APC(Asynchronous Procedure 5. 詳細內容於5.2.6節 非同步程序呼叫 • NextProcessor、DeferredProcessor sizeof(FX_SAVE_AREA) Calls) information。 關於處理器排程的選擇。 4. StackBase KWAIT_BLCOCK、 KWAIT_BLCOCK • AdjustReason、AdjustIncrement KTHREAD、DISPATHER • 記錄目前Stack的基底位置(高位址)。 資料結構 優先層級調整原因和調整量。 關係圖 5. InitialStack和StackBase是相等的,都指向 Kernel stack高位址。 35 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 37 3.4.2 獲得目前的行程和緒程 typedef struct _KTHREAD { SCHAR Priority; – 定義此Thread的優先層級,在執行過程中會變動 SCHAR BasePriority; – 靜態的優先層級,初始值是所屬Process的BasePriority值,可透過 KeSetBasePriority函式重新設定 SCHAR PriorityDecrement; – 紀錄其優先層級的變動值 – 優先層級在Windows分成兩層: • 0~15:普通thread的優先層級 • 16~31:即時thread的優先層級 • 以上兩個區域彼此事不會互相跨越 CHAR Saturation; – 說明此Thread相對於process的基本優先層級,調整量是否超過了整個區間的一 半,值為 0,1,-1 } KTHREAD, *PKTHREAD, *PRKTHREAD; 38 typedef struct _KTHREAD { UCHAR EnableStackSwap; – 為布林值,指出是否允許換出到外部記憶體。 volatile UCHAR SwapBusy; – 為布林值,指出thread是否正在進行context swap。 BOOLEAN Alerted[MaximumMode]; – 為指定該thread在哪種警告模式下可以喚醒 • 在WRK中,警告模式只有使用者模式與核心模式,所以這個陣列含意是指 thread分別在這兩個模式下是否可以喚醒。 } KTHREAD, *PKTHREAD, *PRKTHREAD; 41 typedef struct _KTHREAD { union { LIST_ENTRY WaitListEntry; – Thread在等待執行時,其會當作一個節點加入到某個序列中,也 就是KPROCESS中的WaitListEntry為起始的串列。 SINGLE_LIST_ENTRY SwapListEntry; – 當thread的核心堆疊要換入時,插入到KiStackImSwapListHead。 – 當Thread處於DefferedReady的狀態時,其會將SwapListEntry插 入至DefferedReadyList Head串列中。 }; } KTHREAD, *PKTHREAD, *PRKTHREAD; 42 typedef struct _KTHREAD { PRKQUEUE Queue; – 為一個佇列發送器(分派器),如果不為NULL表示目前正處理此佇列 – 佇列機制,參考第5章 ULONG WaitTime; – 紀錄了一個Thread進入等待時刻的時間點(低32位元),好讓平衡集 管理員可以根據這選項做出相對應的決策 } KTHREAD, *PKTHREAD, *PRKTHREAD; 43 typedef struct _KTHREAD { union { struct { SHORT KernelApcDisable; SHORT SpecialApcDisable; 1. 分別為16位元整數值,0:表示不禁止APC,負數表示禁止APC。 2. 因為多種因素造成要禁止他時,就會不停加上負值,直到消除時,才將這 個因素造成的負值補回,直到變成0為止。 3. 且只有當兩個欄位都為0時,才允許插入APC。 4. 2種核心模式APC,參考第5章(5.2.6節) }; ULONG CombinedApcDisable; 兩欄位或者合 併成一個欄位 }; } KTHREAD, *PKTHREAD, *PRKTHREAD; 44 typedef struct _KTHREAD { PVOID Teb; – 指向Process位址空間的一個TEB結構。 } KTHREAD, *PKTHREAD, *PRKTHREAD; 45 typedef struct _KTHREAD { KTIMER Timer; – 是一個附在thread上的計時器,在實做可逾時的等待函式時 (KeWaitForSingleObject or KeWaitForMultiple)可以用到 union { struct { LONG AutoAlignment : 1; LONG DisableBoost : 1; LONG ReservedFlags : 30; }; LONG ThreadFlags; 這兩個欄位是繼承Kprocess中的 同名欄位,所以用途相同。 }; } KTHREAD, *PKTHREAD, *PRKTHREAD; 48 typedef struct _KTHREAD { union { KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1]; 有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; }; WaitBlock是一個內建陣 列,如果Thread等待的物 件數量小於4不用另外分 配KWAIT_BLOCK物件記 憶體 如果Thread等待的物件數 量大於4要另外分配 } KTHREAD, *PKTHREAD, *PRKTHREAD; 49 typedef struct _KTHREAD { LIST_ENTRY QueueListEntry; – 紀錄thread處理一個佇列項目時,他加入到佇列物件的thread串列中 的節點位址。 } KTHREAD, *PKTHREAD, *PRKTHREAD; 50 typedef struct _KTHREAD { PKTRAP_FRAME TrapFrame; – 紀錄控制流程狀態data structure – TrapFrame欄位是一個thread中最關鍵的部分。 – 因為在windows中,thread式系統排程的基礎,代表一個行程中的一 個控制流程。當一個thread離開執行狀態時,他目前的執行狀態都 必續被記錄下來,以便下次來輪到這個thread執行時,可以恢復原 來的執行狀態。 – 例如:指令指標(IP)在指到哪裡、各個register的值是多少…等等。 PVOID CallbackStack; – 包含thread的callback stack address,當thread從kernel mode呼叫 到user mode時使用 } KTHREAD, *PKTHREAD, *PRKTHREAD; 51 typedef struct _KTHREAD { PVOID ServiceTable; – 指向該thread使用的系統服務表(KeServiceDescriptorTable) UCHAR IdealProcessor; – 指明在多處理器的機器上,該thread的理想處理器 BOOLEAN Preempted; – 表該thread是否被高優先層級的thread搶佔了 – 只有當一個thread正在執行或者正在等待執行而被高優先層級的thread 給搶暫時,這個值才會被設定為TRUE } KTHREAD, *PKTHREAD, *PRKTHREAD; 52 typedef struct _KTHREAD { BOOLEAN ProcessReadyQueue; – 表一個thread是否在所屬的行程KPROCESS 物件的ReadyListHead串列中 BOOLEAN KernelStackResident; – 說明該thread的核心堆疊是否駐留在記憶體中 – 當換入記憶體時在設定成TRUE } KTHREAD, *PKTHREAD, *PRKTHREAD; 53 typedef struct _KTHREAD { KAFFINITY Affinity; 1. 指定了thread的處理器親和性 2. Affinity 此值初始時繼承自行程物件的Affinity 值 3. 為thread指定的處理器集合必須是其行程的 親和性處理器集合的子集合。 4. 在thread執行過程中,Affinity的值有兩種可 能: • 系統親和性,當該thread執行系統工作時透過 KeSetSystemAffinityThread函式來設置。 • Thread本身的親和性,稱為使用者親和性,透過 KeRevertToUserAffinityThread函式來設置。 } KTHREAD, *PKTHREAD, *PRKTHREAD; 54 typedef struct _KTHREAD { KAFFINITY UserAffinity; – 是thread的使用者親和性 – 此值初始時繼承自行程物件的Affinity值,之後可以透過核心函 式KeSetAffinityThread改變thread的使用親和性。 PKPROCESS Process; – 指向thread的行程物件 – Process 在thread初始化時指定 UCHAR ApcStateIndex; – 指目前APC狀態在ApcStatePointer欄位中的索引 PKAPC_STATE ApcStatePointer[2]; – 陣列元素的型別是指向KAPC_STATE的指標,兩個元素分別指向 thread物件的ApcState和SavedApcState } KTHREAD, *PKTHREAD, *PRKTHREAD; 55 typedef struct _KTHREAD { PVOID Win32Thread; – 指向由Windows子系統管理的區域 union { KAPC SuspendApc; }; union { KSEMAPHORE SuspendSemaphore; struct { 兩個Union欄位相互間有聯繫: SuspendApc 被初始化成一個專門的 APC,當該APC被插入並交付時, KiSuspendThread函式被執行,其執行 結果是在thread的SuspendSemaphore 旗號上等待,直到該信號物件有信號, 然後thread被喚醒並且繼續執行。 thread的suspend操作正是透過此一機制來 實作的。 thread的resume操作則是透過控制 SuspendSemaphore 旗號的計數來實作的。 UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH]; ULONG SListFaultCount;}; }; } KTHREAD, *PKTHREAD, *PRKTHREAD; 56 typedef struct _KTHREAD { LIST_ENTRY ThreadListEntry; – 表一個雙串列上的節點 – 當一個thread 被建立時,他被加入到行程物件的ThreadListHead 串列 中 PVOID SListFaultAddress; – 與user mode互鎖單串列POP操作的錯誤處理有關 – 紀錄了上一次user mode互鎖單串列操作發生分頁錯誤的address } KTHREAD, *PKTHREAD, *PRKTHREAD; 57 1. 由於windows的thread排程演算法較複雜,以及他需要支援某些 硬體結構特性,所以KPROCESS and KTHREAD結構中,有些成 員的引入直接跟這些特性有關。 2. 核心層的process和thread object只包含了系統資源管理和多控 制流程並行執行所涉及的基本資訊。 3. 不含與應用程式相關聯的資訊(ex: process映像檔案和thread啟動 函式位址)。 4. Process Object 提供了thread的基本執行環境。 5. 包含process address space和一組process範圍內公用的參數。 6. Thread Object 提供了為參與thread 排程而必須的各種資訊及其 維護控制流程的狀態。 58 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 59 3.4.2 獲得目前的行程和緒程 • 執行體層(Executive)提供各種管理策略, 並為上層應用程式提供基本功能介面 • 每一個process都由一個executive process (EPROCESS) block表示 • 每一個thread又由一個executive thread (ETHREAD) block表示 60 typedef struct _EPROCESS { KPROCESS Pcb; EX_PUSH_LOCK ProcessLock; LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime; EX_RUNDOWN_REF RundownProtect; HANDLE UniqueProcessId; LIST_ENTRY ActiveProcessLinks; 61 Field Name Description KPROCESS Pcb KPROCESS的內嵌結構體。 系統內部一個行程的KPROCESS物件和 EPROCESS物件位址相同 EX_PUSH_LOCK ProcessLock Push lock物件! 用於保護EPROCESS中的資料成員 LARGE_INTEGER CreateTime 行程的建立時間 LARGE_INTEGER ExitTime 行程的結束時間 EX_RUNDOWN_REF RundownProtect 行程的停止保護鎖。 當一個行程最後被銷毀時,要等到其他所有 行程和緒程都釋放此鎖,才可繼續進行 HANDLE UniqueProcessId 行程的唯一編號,在行程建立時設定 LIST_ENTRY ActiveProcessLinks 雙串列節點! 在Windows中,所有活動行程都連接在一起, 構成一個雙串列 62 SIZE_T SIZE_T SIZE_T SIZE_T SIZE_T QuotaUsage [PsQuotaTypes]; QuotaPeak [PsQuotaTypes]; CommitCharge; CommitChargeLimits; CommitChargePeak; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; 高峰值 65 Field Name Description SIZE_T QuotaUsage [PsQuotaTypes] 行程的記憶體使用量 SIZE_T QuotaPeak [PsQuotaTypes] 行程的記憶體尖峰使用量 SIZE_T CommitCharge 行程的virtual memory中page已使用的 數量 SIZE_T CommitChargeLimits 可使用page數量的最大值 SIZE_T CommitChargePeak 尖峰時刻已使用的page數量 SIZE_T VirtualSize 行程的virtual memory大小 SIZE_T PeakVirtualSize virtual memory大小的尖峰值 66 LIST_ENTRY SessionProcessLinks; PVOID DebugPort; PVOID ExceptionPort; PHANDLE_TABLE ObjectTable; EX_FAST_REF Token; ... } EPROCESS, *PEPROCESS; 67 Field Name Description LIST_ENTRY SessionProcessLinks 雙串列節點。 當行程加入到一個系統工作階段中時,它會 作為一個節點加入該工作階段的行程串列中 PVOID DebugPort 控制代碼(handle) 指向偵錯埠 PVOID ExceptionPort 控制代碼(handle) 指向例外埠 PHANDLE_TABLE ObjectTable 行程的控制代碼表 EX_FAST_REF Token 快速引用。 指向行程的存取權杖,用於該行程的安全存 取檢查 68 typedef struct _EPROCESS { ... PFN_NUMBER WorkingSetPage; KGUARDED_MUTEX AddressCreationLock; KSPIN_LOCK HyperSpaceLock; struct _ETHREAD *ForkInProgress; ULONG_PTR HardwareTrigger; 69 Field Name Description PFN_NUMBER WorkingSetPage 指包含行程工作集的page KGUARDED_MUTEX AddressCreationLock guarded mutex, 保護對位址空間的操作 KSPIN_LOCK HyperSpaceLock Spin lock。 保護行程的超空間(hyper space) struct _ETHREAD * ForkInProgress 指向正在複製位址空間的thread ULONG_PTR HardwareTrigger WRK中沒有使用。 紀錄硬體錯誤效能分析次數 70 PMM_AVL_TABLE PhysicalVadRoot; PVOID CloneRoot; PFN_NUMBER NumberOfPrivatePages; PFN_NUMBER NumberOfLockedPages; PVOID Win32Process; struct _EJOB *Job; PVOID SectionObject; PVOID SectionBaseAddress; PEPROCESS_QUOTA_BLOCK QuotaBlock; 71 Field Name Description PMM_AVL_TABLE PhysicalVadRoot 指向行程實體VAD樹的根! PVOID CloneRoot 指向一個平衡樹的根 PFN_NUMBER NumberOfPrivatePages 行程私有page的數量 PFN_NUMBER NumberOfLockedPages 行程被鎖住的page數量 PVOID Win32Process 指向由Windows子系統管理的行程區域的指標 struct _EJOB *Job 本書不討論Job物件 PVOID SectionObject 代表行程記憶體區段物件的指標 PVOID SectionBaseAddress 該記憶體區段物件的基底位址 PEPROCESS_QUOTA_BLOCK QuotaBlock 指向行程的配額區塊 72 PPAGEFAULT_HISTORY WorkingSetWatch; HANDLE Win32WindowStation; HANDLE InheritedFromUniqueProcessId; PVOID PVOID PVOID PVOID LdtInformation; VadFreeHint; VdmObjects; DeviceMap; ... } EPROCESS, *PEPROCESS; 74 Field Name Description PPAGEFAULT_HISTORY WorkingSetWatch 用於監視一個行程的page fault, 由全域變數PsWatchEnabled控制 HANDLE Win32WindowStation 一個行程所屬的視窗工作站的控制代碼 HANDLE 父行程的PID InheritedFromUniqueProcessId PVOID LdtInformation 用來維護一個行程的LDT資訊 (Local Descriptor Table) PVOID VadFreeHint 指向一個提示VAD節點, 用於加速在VAD樹中執行搜尋的操作 PVOID VdmObjects 指向目前行程的VDM資料區。 VDM型別為VDM_PROCESS_OBJECTS, 行程可透過NtVdmControl系統服務來初 始化VDM PVOID DeviceMap 指向行程使用的裝置表 75 typedef struct _EPROCESS { ... PVOID Spare0[3]; Union{ HARDWARE_PTE PageDirectoryPte; ULONGLONG Filler; }; PVOID Session; UCHAR ImageFileName[ 16 ]; LIST_ENTRY JobLinks; PVOID LockedPagesList; 76 Field Name Description PVOID Spare0[3] 待命欄位,WRK沒有使用 HARDWARE_PTE PageDirectoryPte 上層分頁目錄頁面的分頁表項目! PVOID Session 指向行程所在的系統工作階段。 是一個指向MM_SESSION_SPACE的指標 UCHAR ImageFileName[ 16 ] 行程的映像檔名, 僅包含最後一個路徑分隔符號之後的字串 LIST_ENTRY JobLinks 雙串列節點。 一個Job中的所有行程構成一個雙串列! PVOID LockedPagesList 指向LOCK_HEADER結構的指標。 該結構包含一個串列開頭,Windows透過 此串列紀錄哪些page已被鎖住 77 LIST_ENTRY ThreadListHead; PVOID SecurityPort; PVOID PaeTop; ULONG ActiveThreads; ACCESS_MASK GrantedAccess; ULONG DefaultHardErrorProcessing; NTSTATUS LastThreadExitStatus; PPEB Peb; ... } EPROCESS, *PEPROCESS; 80 Field Name Description LIST_ENTRY ThreadListHead 雙串列的頭節點! 此串列包含一個行程中的所有緒程 PVOID SecurityPort 安全埠。 指向該行程與lsass行程之間跨行程通訊埠 PVOID PaeTop 支援PAE記憶體存取機制 ULONG ActiveThreads 記錄目前行程有多少個活動threads ACCESS_MASK GrantedAccess 行程的存取權限 ULONG 指定硬體錯誤處理的預設值 DefaultHardErrorProcessing NTSTATUS LastThreadExitStatus 記錄最後一個thread的結束狀態 PPEB Peb 一個行程的行程環境區塊! 81 typedef struct _EPROCESS { ... EX_FAST_REF PrefetchTrace; LARGE_INTEGER LARGE_INTEGER LARGE_INTEGER LARGE_INTEGER LARGE_INTEGER LARGE_INTEGER ReadOperationCount; WriteOperationCount; OtherOperationCount; ReadTransferCount; WriteTransferCount; OtherTransferCount; 84 Field Name Description EX_FAST_REF PrefetchTrace 快速引用。 指向與該行程關聯的一個預取痕跡結構 LARGE_INTEGER ReadOperationCount 目前行程NtReadFile系統服務被呼叫次數 LARGE_INTEGER WriteOperationCount 目前行程NtWriteFile系統服務被呼叫次 數 LARGE_INTEGER OtherOperationCount 記錄除讀寫操作以外的其他I/O服務次數 LARGE_INTEGER ReadTransferCount 記錄I/O讀取操作完成次數 LARGE_INTEGER WriteTransferCount 記錄I/O寫入操作完成次數 LARGE_INTEGER OtherTransferCount 記錄非讀寫操作完成次數 85 PVOID AweInfo; SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo; MMSUPPORT Vm; LIST_ENTRY MmProcessLinks; ULONG ModifiedPageCount; ULONG JobStatus; 86 Field Name Description PVOID AweInfo 指向AWEINFO結構的指標。 以支援AWE! SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo 包含建立行程時指定的行程映像檔全 路徑名稱 MMSUPPORT Vm Windows為每個行程管理虛擬記憶體 的資料結構成員 LIST_ENTRY MmProcessLinks 雙串列節點! 所有擁有自己位址空間的行程都加入 到這個雙串列中 ULONG ModifiedPageCount 記錄行程已修改的page數量 ULONG JobStatus 記錄行程所屬Job狀態 87 union{ ULONG Flags; struct { …… }; }; NTSTATUS ExitStatus; USHORT NextPageColor; union{ struct{ UCHAR SubSystemMinorVersion; UCHAR SubSystemMajorVersion; }; USHORT SubSystemVersion; }; UCHAR PriorityClass; MM_AVL_TABLE VadRoot; ULONG Cookie; } EPROCESS, *PEPROCESS 90 Field Name Description ULONG Flags 包含了行程的旗標位元 NTSTATUS ExitStatus 行程的結束狀態 USHORT NextPageColor 用於實體頁面分配演算法 UCHAR SubSystemMinorVersion 行程的子系統主版本序號 UCHAR SubSystemMajorVersion 行程的子系統次版本序號 UCHAR PriorityClass 行程的priority MM_AVL_TABLE VadRoot 指向一個平衡二元樹的根 ULONG Cookie 一個代表行程的隨機值。 透過NtQueryInformationProcess函式獲取 91 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 92 3.4.2 獲得目前的行程和緒程 在Windows下,會給每個Process 建立一個EPROCESS結構,相對應 的緒程也會建立一個ETHREAD結構 而ETHREAD結構的第一個成員會是 KTHREAD結構。 每個行程的緒程的ETHREAD結構都 會按右圖所示連結起來 圖源 http://kost0911.pixnet.net/blog/post/61607285 http://flylib.com/books/en/1.242.1.65/1/ 93 typedef struct _ETHREAD 定址一個64 { 位元的有號 KTHREAD Tcb; 數整數 LARGE_INTEGER CreateTime; 記錄執行緒 的建立時間 union { LARGE_INTEGER ExitTime; LIST_ENTRY LpcReplyChain; LIST_ENTRY KeyedWaitChain; }; 所有的緒程會透過 …….. 此結構串在一個雙 向循環串列上 記錄執行緒的 結束時間 用於跨行程通訊 用於帶鍵事件的 等待串列 94 32位元整數,針對驅 動程式的部分去傳回 程式狀態 union { NTSTATUS ExitStatus; PVOID OfsChain; 沒用到! }; 一個指標指向空 的型態(void *) LIST_ENTRY PostBlockList; 結束狀態 向"組態管理員 (Reporting Services)" 登記"登錄機碼"的變化 通知所有已 通知 登記要接收 其終止事件 的那些port union { PTERMINATION_PORT TerminationPort; struct _ETHREAD *ReaperLink; 僅在thread結束時 PVOID KeyedWaitValue; 使用 ,使kernel 帶鍵事件的按鍵值 }; stack可回收 97 LIST_ENTRY ActiveTimerListHead; ULONG ActiveTimerListLock; 參數 UniqueProcess Cid; CLIENT_ID UniqueThread 包含目前緒 程中的所有 timer 控制此list的 spin lock 意義 包含thread的 所屬process的UniqueProcessID 唯一識別字 process控制代碼表中的控制代碼 用於LPC應 union { 答通知 KSEMAPHORE LpcReplySemaphore; KSEMAPHORE KeyedWaitSemaphore; 用於處理 帶鍵的事 }; 98 件 帶有要回覆應 union { 答的訊息 PVOID LpcReplyMessage; PVOID LpcWaitingOnPort; 說明在哪個 }; 指向thread base\ntos\lpc\lpcp.h port上等待 的模仿資訊 PPS_IMPERSONATION_INFORMATION ImpersonationInfo; LIST_ENTRY IrpList; ULONG TopLevelIrp; 包含目前thread 所有正在處理但 未完成的I/O請求 指向thread的上層IRP,或指向 NULL或一個IRP 99 struct _DEVICE_OBJECT *DeviceToVerify 指向目前所 屬的 process PERPROCESS ThreadsProcess; PVOID StartAddress; 指向一個待 檢驗的裝置 包含thread 的真正啟動 位址 union { PVOID Win32StartAddress; ULONG LpcReceivedMessageId; windows子系 統接受到的 thread啟動位 址 }; 接收到的LPC訊息ID 101 LIST_ENTRY ThreadListEntry; thread加到所屬 Eprocess結構的 ThreadListHead中 EX_RUNDOWN_REF RundownProtect; EX_PUSH_LOCK ThreadLock; thread的停止 protected lock 用來保護thread的 資料屬性 ULONG LpcReceivedMessageId; 等待要回覆的 MessageId 102 ULONG ReadClusterSize; 一次I/O操作要讀 取幾個頁面 ACCESS_MASK GrantedAccess; public\sdk\inc\ntpsapi.h union { ULONG CrossThreadFlags; struct {...}; }; 存取權限 針對跨thread存取 的flag位元 103 union { ULONG SameThreadPassiveFlags; 只能在最低中斷層 struct {...}; 級才可存取,且只 能被自身存取 }; union { ULONG SameThreadApcFlags; struct {...}; }; 在APC中斷層 級上被自身存 取的flag 105 BOOLEAN ForwardClusterOnly; 是否僅向前聚集? BOOLEAN DisablePageFaultClustering; 控制頁面交 換的聚集與 否 UCHAR ActiveFaultCount; } ETHREAD, *PETHREAD; 正在進行的分 頁錯誤數量 106 章節地圖 3.1 行程基本觀念 3.1.1 多行程模型 3.1.2 行程與程式 3.2 緒程基本觀念 3.2.1 緒程模型 3.2.2 緒程排程演算法 3.2.3 緒程與行程的關係 3.3 Windows 的行程和緒程資料結構 3.3.1 核心層的行程和緒程物件 3.3.2 執行體層的行程和緒程物件 3.4 Windows 行程和緒程的管理 3.4.1 Windows 行程的控制代碼表 107 3.4.2 獲得目前的行程和緒程 • “In computer programming, a handle is a particular kind of smart pointer.” • The Windows API heavily uses handles to: – represent objects in the system – provide a communication pathway between the operating system and user space. • For example, a window on the desktop is represented by a handle of type HWND (handle, window). http://en.wikipedia.org/wiki/Handle_%28computing%29 108 • 在 Windows 中 – handle是process範圍內的物件參考 – handle僅在一個process範圍內才有效 – handle是一個索引,指向所在process的handle table中的一個項目 – 一個handle table包含所有已被該process開啟的 物件之指標 109 • 在 Windows Server 2003 中 – handle table為一多層結構,資料型態為HANDLE_TABLE – 每一個handle table entry結構為HANDLE_TABLE_ENTRY, 其大小為8 bytes – Windows執行體在分配handle table記憶體時按照分頁 (4KB)來申請記憶體 – 每申請一個分頁來存放handle table entry,handle table 容量增加512 – 這些結構都被定義在base\ntos\inc\ex.h 110 111 typedef struct _HANDLE_TABLE { ULONG_PTR TableCode; // 指向handle table最頂層的指標 struct _EPROCESS *QuotaProcess; // 紀錄handle table記憶體資源的process HANDLE UniqueProcessId; // process ID,用於callback function EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS]; // 僅在擴充handle table時使用。 HANDLE_TABLE_LOCKS = 4 LIST_ENTRY HandleTableList; // 所有handle table所形成的串列 EX_PUSH_LOCK HandleContentionEvent; // 當race condition發生時使用 PHANDLE_TRACE_DEBUG_INFO DebugInfo; // 偵錯資訊,偵錯時方有意義 112 LONG ExtraInfoPages; // 稽核資訊所佔用的分頁數量 ULONG FirstFree; // 紀錄空閒handle的索引值 ULONG LastFree; // 紀錄最近被釋放之handle的索引值 ULONG NextHandleNeedingPool;// 下一次擴充handle table的起始索引值 LONG HandleCount; // 使用中handle的數量 union { ULONG Flags; BOOLEAN StrictFIFO : 1; }; } HANDLE_TABLE, *PHANDLE_TABLE; 113 • TableCode是一個指向handle table最頂層頁面的指標,其最低2位 元代表目前table的層數: – 00: 僅一層,該process最多只能容納512 handles (LOWLEVEL_THRESHOLD) – 01: 兩層,process可容納512*1024 handles (MIDLEVEL_THRESHOLD) – 10: 三層,process可容納512*1024*1024 handles (HIGHLEVEL_THRESHOLD) • Windows 執行體限制每個process的handle數目不得超過224 = 16,777,216個 (MAX_HANDLES) • 實際上,每個最低層handle table頁面的第一個handle table entry 有特殊用途,真正可供process使用的handles只有512 - 1個 114 115 • 執行體在建立process時,會先為其分配一單層的 handle table • handle table是透過ExCreateHandleTable函式來 完成的 • 隨著process中的handle數量增加, ExpAllocateHandleTableEntry函式會呼叫 ExpAllocateHandleTableEntrySlow函式將handle table擴充到兩層至三層 • 上述函式的實作程式碼位於 base\ntos\ex\handle.c 116 NTKERNELAPI PHANDLE_TABLE ExCreateHandleTable ( __in_opt struct _EPROCESS *Process ) { PKTHREAD CurrentThread; PHANDLE_TABLE HandleTable; PAGED_CODE(); CurrentThread = KeGetCurrentThread (); HandleTable = ExpAllocateHandleTable( Process, TRUE ); // 建構初始的handle table if (HandleTable == NULL) { return NULL; } KeEnterCriticalRegionThread (CurrentThread); ExAcquirePushLockExclusive( &HandleTableListLock ); InsertTailList( &HandleTableListHead, &HandleTable->HandleTableList ); // 將handle table 插入list ExReleasePushLockExclusive( &HandleTableListLock ); KeLeaveCriticalRegionThread (CurrentThread); return HandleTable; } 117 typedef struct _HANDLE_TABLE_ENTRY { union { // 指向此handle所代表的物件 PVOID Object; ULONG ObAttributes; // 最低三位元具有特別意義 PHANDLE_TABLE_ENTRY_INFO InfoTable; //紀錄每個handle table分頁第一個entry ULONG_PTR Value; }; union { union { ACCESS_MASK GrantedAccess; // 存取遮罩 struct { USHORT GrantedAccessIndex; USHORT CreatorBackTraceIndex; }; }; LONG NextFreeTableEntry; // 空閒時表示下一個空閒handle之索引 }; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; 118 • Object指標指向了handle所代表的kernel object • 其最低3位元具有特別的含義: – 第0位: OBJ_PROTECT_CLOSE,表示呼叫者是否允許關閉該handle // 在WRK中,關閉保護位元被移到GrantedAccess成員, // 而此位元變成handle table entry的lock flag – 第1位: OBJ_INHERIT,指示該process之sub process可否繼承該handle – 第2位: OBJ_AUDIT_OBJECT_CLOSE,指示關閉該object時是否產生一稽 核事件 119 • 一個有效的handle有四種可能的值: – -1 : 代表目前的process – -2 : 代表目前的thread – <0 : 其絕對值為kernel handle table的索引,僅限kernel mode的函式 使用 – < 226 : 目前process的handle table索引 • kernel handle table – 是指系統空間中的global handle table – 即WRK中的ObpKernelHandleTable變數 – 只能在kernel mode下被使用 – 可以位於任何一個process environment中,故為跨行程環境 120 121 • 解析handle的基本函式是ObReferenceObjectByHandle • 其程式碼位於base\ntos\ob\obref.c • 首先判斷傳入之handle值屬於前述4種情形的哪一種 – -1 或 -2 : 無需搜尋handle table,檢查process/thread存取權限 後即可回傳物件 – <0 或 <226 : 1. 2. 3. 4. 先呼叫ExMapHandleToPointerEx函式找到該handle索引值所指到的 handle entry 並檢查其存取權限是否滿足要求 再呼叫ObpAuditObjectAccess進行稽核 最後回傳物件主體 (ExMapHandleToPointerEx函式呼叫ExpLookupHandleTableEntry函式來 122 搜尋handle table到最底層的項目) • 將物件插入到handle table的是ObInsertObject函式 • 其程式碼位於base\ntos\ob\obinsert.c • 函式簡略流程: – 首先對參數作各種檢查 – 呼叫ObpCreateHandle函式 1. 為要插入的物件建立handle 2. 呼叫ExCreateHandle建立handle entry 123 • 物件的引用方式有兩種: 1. 在kernel中透過物件的位址來引用 • 這是透過ObReferenceObjectByPointer函式來記 錄引用次數 2. 透過handle來引用物件 • 由ObpIncrementHandleCount函式來檢查並記 錄handle的引用 124 • • • 在WRK中, handle的生命週期的起點為: – 被插入到handle table – ObpCreateHandle → ObpIncrementHandleCount handle生命週期的終點: – handle被關閉 – ObpCloseHandle → ObpCloseHandleTableEntry → ObpDecrementHandleCount 此外,當handle呼叫ObReferenceObjectByHandle後,若該物件 指標不再使用,必須呼叫ObDeferenceObject 125 1. 作為物件引用的容器 2. 分配process、thread的unique ID – Process : UniqueProcessId – Thread : Cid – ExCreateHandle在全域的handle table叫做PspCidTable (Client ID handle table)中建立的handle索引值 – PspCidTable中的handle entry包含process或thread的物 件位址 126 • process和thread的unique ID都是4的倍數 – 0 是專門保留給空閒的process • 並非透過ExCreateHandle函式獲得 – 4 是第一個handle索引值,被分配給System process的unique ID • CID handle table按照Strict FIFO來重用handle entry • 在kernel中,根據process/thread的唯一ID可方便找到相關物件位址 – PsLookupProcessThreadByCid – PsLookupProcessByProcessId – PsLookupThreadByThreadId (上述三函式位於base\ntos\pscid.c) 127 • KeGetCurrentThread函式傳回目前處理器 上正在執行的緒程的KTHREAD結構指標 • 透過此結構資訊,進一步可得到 KPROCESS、ETHREAD和EPROCESS結構。 128 FORCEINLINE struct _KTHREAD * NTAPI KeGetCurrentThread (VOID) { #if (_MSC_FULL_VER >= 13012035) return (struct _KTHREAD *) (ULONG_PTR) __readfsdword (FIELD_OFFSET (KPCR, PrcbData.CurrentThread)); #else __asm { mov eax, fs:[0] KPCR.PrcbData.CurrentThread } #endif } • 以上程式碼實際上是用來存取FS暫存器上特定偏移的指令 • 藉此以取得目前Thread之KTHREAD結構指標 129 • FS暫存器指向一塊被稱為處理器控制區的 記記憶體(PCR),其資料型別為KPCR。 • KPCR有一個型別為KPRCB的資料成員 PrcbData,這是目前處理器的控制區塊 (Processor Control Block),其中包含了指 向目前緒程的KTHREAD結構的指標。 130 • KPRCB 是一個 structure,在_KPRCB structure 包含了: struct _KTHREAD *CurrentThread; < = target struct _KTHREAD *NextThread; struct _KTHREAD *IdleThread; • 我們能從其中一項結構找到 CurrentThread 的所在 131 • 獲得了目前緒程的KTHREAD結構的指標後, 便可方便地獲得ETHREAD結構的指標,以 及KPROCESS或EPROCESS結構的指標。 • 在執行體層上獲得目前緒程或行程的函式 分別是PsGetCurrentThread和 PsGetCurrentProcess。 132 #define _PsGetCurrentProcess() (CONTAINING_RECORD \ (((KeGetCurrentThread()) -> ApcState.Process), EPROCESS, Pcb)) #define _PsGetCurrentThread() ((PETHREAD) KeGetCurrentThread()) PEPROCESS PsGetCurrentProcess(VOID) { return _PsGetCurrentProcss() ; } PETHREAD PsGetCurrentThread(VOID) { return _PsGetCurrentThread() ; } 133 • _PsGetCurrentProcess從目前緒程KTHREAD 結構的ApcState成員中獲得目前緒程所屬行程 的KPROCESS結構。 • 即使目前緒程附加到其他行程中,或又回到原 先行程中,ApcState總能獲得正確的目前行程 結構的指標,故不是從KTHREAD的Process欄 位或ETHREAD的ThreadsProcess欄位獲得行 程結構的指標。 • 作法請參考5.2.6節 APC環境的敘述 134 135 • Process – http://zh.wikipedia.org/wiki/%E8%A1%8C%E7%A8%8B – http://zh.wikipedia.org/wiki/%E8%A1%8C%E7%A8%8B%E6%8E%A 7%E5%88%B6%E8%A1%A8 – http://en.wikipedia.org/wiki/Process_control_block – http://www.personal.kent.edu/~rmuhamma/OpSystems/Myos/proc essControl.htm • Thread http://ppt.cc/dnzk http://programming.im.ncnu.edu.tw/J_Chapter9.htm http://ccckmit.wikidot.com/thread http://zh.wikipedia.org/wiki/%E5%9F%B7%E8%A1%8C%E7%B7%92 http://wiki.answers.com/Q/What_is_the_difference_between_user_le vel_threads_and_kernel_level_threads – http://nixchun.pixnet.net/blog/post/7044425-os---5.-threads – – – – – 136 • Operating System Concepts, 8/e (IE-Paperback) • • • – Abraham Silberschatz, Peter Baer Galvin, Greg Gagne http://www.nirsoft.net/kernel_struct/vista/KPROCESS.html http://doxygen.reactos.org/df/d2c/structKPROCESS_adf7c1ea8dabdb9 aad4ea79dc089ac110.html#adf7c1ea8dabdb9aad4ea79dc089ac110 http://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web& cd=5&ved=0CIABEBYwBA&url=http%3A%2F%2Fi-web.i.utokyo.ac.jp%2Fedu%2Ftraining%2Fss%2Flecture%2Fnewdocuments%2FLectures%2F13-Processes%2FProcesses.ppt&ei=CrGsTH1KOHUmAXWyaWoBA&usg=AFQjCNF1eR9Y5RHYyLve8rZxC0oePurt8 A&sig2=Yx2jydJtDuDuYSCmIfKxwg 137 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. EPROCESS PUSH LOCK LIST_ENTRY CommitCharge FAST_REF(P.28) EX_FAST_REF Working Set Working Set Hyper Space VAD EJOB LDT MM_SESSION_SPACE PAE AWE Vista EPROCESS 138 • • • • • • • • • • • • • • • • http://msdn.microsoft.com/zh-tw/library/ms123401 http://hi.baidu.com/solohac/blog/item/b536d02ef45b67321f3089cd.html http://blog.csdn.net/imquestion/article/details/16423 http://kost0911.pixnet.net/blog/post/61607285 http://flylib.com/books/en/1.242.1.65/1/ http://news.ccidnet.com/art/32859/20100714/2114941_2.html http://blog.csdn.net/better0332/article/details/4292433 http://doxygen.reactos.org/dd/d05/ndk_2pstypes_8h_source.html#l00877 http://book.51cto.com/art/201011/235760.htm http://bazislib.sysprogs.org/dox/a00166.html http://puremonkey2010.blogspot.com/2011/01/windows-ddp_31.html http://en.wikipedia.org/wiki/I/O_request_packet http://fsinternals.com/?p=29 http://pollos-blog.blogspot.com/2007/09/windows-driver-programming5.html http://www.nirsoft.net/kernel_struct/vista/ETHREAD.html http://msdn.moonsols.com/msdn 139 140