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