1. 程式人生 > >4.WaitForSingleObject函式分析

4.WaitForSingleObject函式分析

無論可等待物件是何種型別,執行緒都是通過:

WaitForSingleObject WaitForMultipleObjects

進入等待狀態的,這兩個函式是理解執行緒等待與喚醒進位制的核心

WaitForSingleObject引數說明

WaitForSingleObject對應的核心函式:

NTSTATUS stdcall NtWaitForSingleObject
( 
	HANDLE Handle,					
	BOOLEAN Alertable,				
	PLARGE_INTEGER Timeout	
)

Handle 使用者層傳遞的等待物件的控制代碼 (具體細節參加控制代碼表專題)

Alertable 對應 KTHREAD 結構體的 Alertable 屬性如果為1在插入使用者APC時,該執行緒將被吵醒

Timeout 超時時間

NtWaitForSingleObject

  1. 呼叫ObReferenceObjectByHandle函式,通過物件控制代碼找到等待物件結構體地址。
  2. 呼叫KeWaitForSingleObject函式,進入關鍵迴圈。

KeWaitForSingleObject:上半部分

!process 89316020
dt _KTHREAD 892db020
dt _KWAIT_BLOCK 892db020+70

每個等待塊大小為0x18。 在這裡插入圖片描述 如果等待3個物件的話它就會佔用前3個等待塊,最後一個是給定時器用的。 如果你有4個等待物件它就不會用這個位置了,它會一次性分配新的空間。

  1. 向KTHREAD(+70)位置的等待塊賦值。
  2. 如果超時時間不為0, KTHREAD(+70)第四個等待塊與第一個等待塊關聯起來:第一個等待塊指向第四個等待塊,第四個等待塊指向第一個等待塊。
  3. KTHREAD(+5C)指向第一個KWAIT_BLOCK
  4. 進入關鍵迴圈

KeWaitForSingleObject的關鍵迴圈

while(true)//每次執行緒被其他執行緒喚醒,都要進入這個迴圈
	{
		if(符合啟用條件)//1超時 2等待物件SignalState > 0
		{
		//1修改SignalState 
		//2退出迴圈
		}
		else//SignalState不大於0 也沒超時
		{
			if
(第一次執行) { //將當前執行緒的等待塊掛到等待物件的連結串列 (WaitListHead) 中; //將自己掛入等待佇列(KiaitListHead) //切換執行緒...再次獲得CPU時,從這裡開始執行 } } } 1)執行緒將自己+5c位置清0 2)釋放_KWAIT_BLOCK所佔記憶體

WaitForSingleObject引數說明

kd> dt _DISPATCHER_HEADER
nt!_DISPATCHER_HEADER
   +0x000 Type			//型別
   +0x001 Absolute 
   +0x002 Size
   +0x003 Inserted
   +0x004 SignalState		//是否有訊號 (值大於0就是有訊號)
   +0x008 WaitListHead	//雙向連結串列頭,圈著所有等待塊

不同的等待物件,用不同的方法來修改_DISPATCHER_HEADER(SignalState)比如:如果可等待物件是EVENT,其他執行緒通常使用SetEvent來設定SignalState= 1並且,將正在等待該物件的其他執行緒喚醒,也就是從等待連結串列(KiWaitListHead)中摘出來。但是, SetEvent函式並不會將執行緒從等待網上摘下來,是否要下來,由當前執行緒自己來決定。

關於強制喚醒 在APC專題講過,當我們插入一個使用者APC時(Alertable=1),當前執行緒是可以被喚醒的,但並不是真正的喚醒。 因為,如果當前的執行緒在等待網上,執行完使用者APC後,仍然要進入等待狀態。