Windows事件等待学习笔记(三)—— WaitForSingleObject函数分析
Windows事件等待學習筆記(三)—— WaitForSingleObject函數分析
- 要點回顧
- WaitForSingleObject
- NtWaitForSingleObject
- KeWaitForSingleObject:上半部分
- 關鍵循環
- 總結
- 關于強制喚醒
- 實驗:證明等待塊與等待塊表的關系
- 第一步:編譯并運行以下代碼
- 第二步:再WinDbg中找到該進程
- 第三步:查看線程信息
要點回顧
無論可等待對象是何種類型,線程都是通過 WaitForSingleObject 和 WaitForMultipleObjects進入等待狀態的,這兩個函數是理解線程等待與喚醒進制的核心
WaitForSingleObject
DWORD WaitForSingleObject(HANDLE hHandle, // handle to objectDWORD dwMilliseconds // time-out interval);對應的內核函數:NtWaitForSingleObject
NTSTATUS __stdcall NtWaitForSingleObject(HANDLE Handle, //用戶層傳遞的等待對象的句柄(具體細節參加句柄表專題)BOOLEAN Alertable, //對應KTHREAD結構體的Alertable屬性//如果為1 在插入用戶APC時,該線程將被吵醒 PLARGE_INTEGER Timeout //超時時間);NtWaitForSingleObject
KeWaitForSingleObject:上半部分
注意:無論使用與否,_KTHREAD(+70)的第四個等待塊被定時器占據,如果用的話,將會把定時器與第一個等待塊相關聯
第一個等待塊指向第四個等待塊,第四個等待塊指向第一個等待塊。
關鍵循環
(每一個線程與等待對象是通過等待塊進行關聯的,但是對象有一個條件:至少有一個成員為 _DISPATCHER_HEADER 結構體)_DISPATCHER_HEADER+0x000 Type //類型 可通過IDA或WinDbg查看所需對象的類型//IDA:分析二進制代碼//WinDbg:Wait一個對象,然后進行查看+0x001 Absolute +0x002 Size +0x003 Inserted +0x004 SignalState //是否有信號(大于0表示有信號) +0x008 WaitListHead //雙向鏈表頭 圈著所有等待塊
完整邏輯:
while(true)//每次線程被其他線程喚醒,都要進入這個循環 {if(符合激活條件)//1、超時 2、等待對象SignalState>0 {//1) 修改SignalState//2) 退出循環}else{if(第一次執行)將當前線程的等待塊掛到等待對象的鏈表(WaitListHead)中;//將自己掛入等待隊列(KiWaitListHead)//切換線程...再次獲得CPU時,從這里開始執行} }退出循環:
總結
不同的等待對象,用不同的方法來修改 _DISPATCHER_HEADER->SignalState
如果可等待對象是EVENT,其他線程通常使用SetEvent來設置SignalState = 1,并且,將正在等待該對象的其他線程喚醒,也就是從等待鏈表(KiWaitListHead)中摘出來,此時線程臨時復活
SetEvent函數并不會將線程從等待網上摘下來,是否要下來,由當前線程自己來決定
若使用SetEvent這種函數直接將線程從等待網上摘下來,將會非常麻煩,因為可能有非常多的線程在等待一個對象,無法判斷該將誰摘下(一個也線程可能等待著多個對象)
比如:線程A和線程B同時在等待著一個對象,這時如果有線程C調用了SetEvent(將等待對象的信號量置1),線程A和線程B會被臨時喚醒(從KiWaitLkistHead摘下),并行進入關鍵循環,假設線程A先運行,線程A會設置等待對象的信號量<=0,然后將自己從等待網上摘下來,此時線程A徹底復活。線程B再去判斷等待對象是否有信號量時,已經沒有信號量了,這時線程B會將自己重新掛入等待鏈表中(有點繞,慢慢理解)
不同對象調用API修改信號個數只在細節上有差異,本質上都是一樣的
關于強制喚醒
描述:在APC專題中講過,當插入一個用戶APC時(Alertable=1),當前線程是可以被喚醒的,但并不是真正的喚醒。因為如果當前的線程在等待網上,執行完用戶APC后,線程仍然要進入等待狀態
實驗:證明等待塊與等待塊表的關系
第一步:編譯并運行以下代碼
#include <stdio.h> #include <windows.h>HANDLE hEvent[3];DWORD WINAPI ThreadProc(LPVOID lpParamter) {::WaitForSingleObject(hEvent[0], -1);printf("ThreadProc函數執行\n");return 0; }int main(int argc, char* argv[]) {hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL); //創建可等待對象::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);getchar();return 0; }第二步:再WinDbg中找到該進程
第三步:查看線程信息
總結
以上是生活随笔為你收集整理的Windows事件等待学习笔记(三)—— WaitForSingleObject函数分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows事件等待学习笔记(二)——
- 下一篇: Windows事件等待学习笔记(四)——