Windows事件等待学习笔记(四)—— 事件信号量互斥体
Windows事件等待學習筆記(四)—— 事件&信號量&互斥體
- 要點回顧
- 事件
- 實驗:驗證SignalState
- 第一步:編譯并運行以下代碼
- 第二步:觀察結果
- 第三步:修改代碼并執(zhí)行
- 第四步:觀察結果
- 第五步:修改代碼并執(zhí)行
- 第六步:觀察結果
- 總結
- 實驗二:驗證Type
- 第一步:編譯并運行以下代碼
- 第二步:觀察結果
- 第三步:修改代碼并執(zhí)行
- 第四步:觀察結果
- 解釋說明
- 分析WaitForSingleObject
- 信號量
- 互斥體
- 創(chuàng)建互斥體
- 分析 WaitForSingleObject
- 釋放互斥體
- 解決遺棄問題
- ApcDisable
要點回顧
事件
創(chuàng)建事件對象API:CreateEvent(NULL, TRUE, FALSE, NULL);
_DISPATCHER_HEADER+0x000 Type //CreateEvent的第二個參數(shù)決定了當前事件對象的類型//TRUE:通知類型對象 FALSE:事件同步對象+0x001 Absolute+0x002 Size+0x003 Inserted+0x004 SignalState //CreateEvent的第三個參數(shù)決定了這里是否有值+0x008 WaitListHead實驗:驗證SignalState
第一步:編譯并運行以下代碼
#include <stdio.h> #include <windows.h>HANDLE g_hEvent;DWORD WINAPI ThreadProc(LPVOID lpParameter) {//當事件變成已通知時WaitForSingleObject(g_hEvent, INFINITE);printf("Thread執(zhí)行了!\n");return 0; }int main() {//創(chuàng)建事件//默認安全屬性 對象類型 初始狀態(tài) 名字g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//設置為有信號//SetEvent(g_hEvent);//創(chuàng)建線程::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);getchar();return 0; }第二步:觀察結果
無任何輸出,g_hEvent句柄處于無信號狀態(tài)
第三步:修改代碼并執(zhí)行
CreateEvent(NULL, FALSE, TRUE, NULL); //第三個參數(shù)由FALSE改為TRUE第四步:觀察結果
第五步:修改代碼并執(zhí)行
CreateEvent(NULL, FALSE, FALSE, NULL); //第三個參數(shù)由TRUE改為FALSE SetEvent(g_hEvent); //新增一行,設置信號量第六步:觀察結果
總結
實驗二:驗證Type
第一步:編譯并運行以下代碼
#include <stdio.h> #include <windows.h>HANDLE g_hEvent;DWORD WINAPI ThreadProc1(LPVOID lpParameter) {//當事件變成已通知時WaitForSingleObject(g_hEvent, INFINITE);printf("Thread1執(zhí)行了!\n");return 0; }DWORD WINAPI ThreadProc2(LPVOID lpParameter) {//當事件變成已通知時WaitForSingleObject(g_hEvent, INFINITE);printf("Thread2執(zhí)行了!\n");return 0; }DWORD WINAPI ThreadProc3(LPVOID lpParameter) {//當事件變成已通知時WaitForSingleObject(g_hEvent, INFINITE);printf("Thread3執(zhí)行了!\n");return 0; }int main() {//創(chuàng)建事件//默認安全屬性 對象類型 初始狀態(tài) 名字g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); HANDLE hThread[3];//創(chuàng)建3個線程hThread[0] = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);hThread[1] = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);hThread[2] = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);//設置事件為已通知SetEvent(g_hEvent);//等待線程結束 銷毀內核對象WaitForMultipleObjects(3, hThread, TRUE, INFINITE);CloseHandle(hThread[0]);CloseHandle(hThread[1]);CloseHandle(hThread[2]);CloseHandle(g_hEvent);getchar();return 0; }第二步:觀察結果
三個線程都得到了執(zhí)行
第三步:修改代碼并執(zhí)行
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //第三個參數(shù)由TRUE改為FALSE第四步:觀察結果
只有Thread1得到了執(zhí)行
解釋說明
分析WaitForSingleObject
總結:
信號量
描述:
優(yōu)點:舉個例子,在生產者與消費者的問題中,若生產者只有三份,那么開五個消費者線程是沒有意義的,信號量的存在正是為了解決這種問題
創(chuàng)建信號量API:
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, //發(fā)放信號量的數(shù)量LONG lMaximumCount, //信號量發(fā)放數(shù)量的最大值LPCTSTR lpName);對應內核結構體:
kd> dt _KSEMAPHORE ntdll!_KSEMAPHORE+0x000 Header : _DISPATCHER_HEADER+0x010 Limit : Int4B //IMaximumCount kd> dt _DISPATCHER_HEADER ntdll!_DISPATCHER_HEADER+0x000 Type : UChar //信號量類型為5+0x001 Absolute : UChar+0x002 Size : UChar+0x003 Inserted : UChar+0x004 SignalState : Int4B //lInitialCount+0x008 WaitListHead : _LIST_ENTRY釋放信號量API:
互斥體
描述:
極端情況:
例:
當構造了一個臨界區(qū)一,等待的對象是A,又在臨界區(qū)內部構造了一個臨界區(qū)二,等待對象為A、B、C三個,當臨界區(qū)一執(zhí)行完自己的功能后,如果等待的對象為事件或者信號量,那么就必須調用相關API將對象設置為有信號,若進入臨界區(qū)二前未調用相關API,那么臨界區(qū)二將永遠進入等待狀態(tài),這種情況稱為死鎖。
當一個對象需要重復進入臨界區(qū)時,若A對象為互斥體,就不會出現(xiàn)死鎖。
結構體:
nt!_KMUTANT+0x000 Header : _DISPATCHER_HEADER+0x010 MutantListEntry : _LIST_ENTRY+0x018 OwnerThread : Ptr32 _KTHREAD+0x01c Abandoned : UChar+0x01d ApcDisable : UCharMutantListEntry:擁有互斥體線程 (KTHREAD+0x010 MutantListHead),是個鏈表頭,圈著當前線程所有的互斥體
OwnerThread:正在擁有互斥體的線程
Abandoned:是否已經被放棄不用
ApcDisable:是否禁用內核APC
創(chuàng)建互斥體
函數(shù):
HANDLE CreateMutex( LPSECURITY_ATTRIBUTE SlpMutexAttributes, // 指向安全屬性的指針 BOOL bInitialOwner, // 初始化互斥對象的所有者 LPCTSTR lpName // 指向互斥對象名的指針 );API調用流:
CreateMutex -> NtCreateMutant(內核函數(shù)) -> KeInitializeMutant(內核函數(shù))
初始化MUTANT結構體:
MUTANT.Header.Type=2; MUTANT.Header.SignalState=bInitialOwner ? 0 : 1; MUTANT.OwnerThread=bInitialOwner ? 當前線程 : NULL; MUTANT.Abandoned=0; MUTANT.ApcDisable=0;bInitialOwner==TRUE 將當前互斥體掛入到當前線程的互斥體鏈表 (KTHREAD+0x010 MutantListHead)分析 WaitForSingleObject
釋放互斥體
API:
BOOL WINAPI ReleaseMutex(HANDLE hMutex);API執(zhí)行流:
ReleaseMutex -> NtReleaseMutant -> KeReleaseMutant
正常調用時:
MUTANT.Header.SignalState++;
如果SignalState=1(即退出最外圈臨界區(qū)后),說明其他進程可以使用了,將該互斥體從線程鏈表中移除
解決遺棄問題
描述:
內核函數(shù)MmUnloadSystemImage會調用KeReleaseMutant(X, Y, Abandon, Z),第三個參數(shù)用來判斷該互斥體是否被丟棄,正常釋放時值為false,有且只有互斥體有這個待遇
ApcDisable
對應內核函數(shù):NtCreateMutant
ApcDisable=0
對應內核函數(shù):NtCreateMutex
ApcDisable=1
注意:若在三環(huán)創(chuàng)建互斥體(Mutant),內核APC仍然可以使用;若通過零環(huán)創(chuàng)建互斥體(Mutex),那么當前內核APC是被禁止的
《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Windows事件等待学习笔记(四)—— 事件信号量互斥体的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows事件等待学习笔记(三)——
- 下一篇: Windows句柄表学习笔记 —— 句柄