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