日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Windows事件等待学习笔记(二)—— 线程等待与唤醒

發布時間:2025/3/21 windows 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows事件等待学习笔记(二)—— 线程等待与唤醒 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Windows事件等待學習筆記(二)—— 線程等待與喚醒

    • 要點回顧
    • 等待與喚醒機制
      • 可等待對象
      • 可等待對象的差異
    • 線程與等待對象
      • 一個線程等待一個對象
        • 實驗
          • 第一步:編譯并運行以下代碼
          • 第二步:在WinDbg中找到該進程
          • 第三步:查看線程信息
      • 一個線程等待多個對象
        • 實驗
          • 第一步:編譯并運行以下代碼
          • 第二步:在WinDbg中找到該進程
          • 第三步:查看線程信息
          • 第四步:查看等待塊具體細節
      • 等待網
      • 總結

要點回顧

之前學習了如何自己實現臨界區以及什么是自旋鎖,這兩種同步方案在線程無法進入臨界區時都會讓當前線程進入等待狀態
一種是通過Sleep函數實現的,一種是通過讓當前的CPU“空轉”實現的,但這兩種等待方式都有局限性:

  • 通過Sleep函數進行等待,等待時間該如何確定呢?
  • 通過“空轉”的方式進行等待,只有等待時間很短的情況下才有意義,否則對CPU資源是種浪費,并且自旋鎖只能在多核的環境下才有意義
  • 思考:有沒有更加合理的等待方式,只有在條件成熟的時候才將當前線程喚醒?

    等待與喚醒機制

    描述:在Windows中,一個線程可以通過等待一個或者多個可等待對象,從而進入等待狀態,另一個線程可以在某些時刻喚醒等待這些對象的其他線程

    可等待對象

    在Windbg中查看以下結構體:

    dt _KPROCESS

    dt _KTHREAD

    dt _KTIMER

    dt _KSEMAPHORE

    dt _KEVENT

    dt _KMUTANT

    dt _FILE_OBJECT


    總結

  • 可等待對象正常情況下都是以 _DISPATCHER_HEADER 結構體開頭的,但是有一些特殊的結構體并不是以 _DISPATCHER_HEADER 開頭的(如 _FILE_OBJECT),但是windows又希望把它們也變成所謂的可等待對象,因此在它們內部嵌入一個 _DISPATCHER_HEADER 這樣的結構體
  • 只要是包含 _DISPATCHER_HEADER 結構體的對象,都可以看作是可等待對象,都可以使用 WaitForSingleObjectWaitForMultipleObjects 這兩個函數進入等待狀態
  • 可等待對象的差異

    WaitForSingleObject(3環)

    NtWaitForSingleObject(內核)

  • 通過3環用戶提供的句柄,找到等待對象的內核地址
  • 如果_DISPATCHER_HEADER 開頭,直接使用。
  • 如果不是_DISPATCHER_HEADER 開頭的對象,則找到在其中嵌入_DISPATCHER_HEADER 對象
  • KeWaitForSingleObject(內核) //核心功能

    線程與等待對象

    描述:一個線程可以等待一個對象,也可以等待多個對象

    一個線程等待一個對象

    實驗

    第一步:編譯并運行以下代碼
    #include <stdio.h> #include <windows.h>HANDLE hEvent[2];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); //創建一個可等待對象 _KEVENT::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);getchar();return 0; }
    第二步:在WinDbg中找到該進程

    !process 0 0



    第三步:查看線程信息

    查看 _KTHREAD

    查看 _KWAIT_BLOCK

    kd> dt _KWAIT_BLOCK 0x85f49278 nt_400000!_KWAIT_BLOCK+0x000 WaitListEntry : _LIST_ENTRY [ 0x86177180 - 0x86177180 ]+0x008 Thread : 0x85f49208 _KTHREAD //所屬線程+0x00c Object : 0x86177178 Void //被等待對象的地址+0x010 NextWaitBlock : 0x85f49278 _KWAIT_BLOCK //單向循環鏈表,只有一個時指向自己+0x014 WaitKey : 0 //等待塊的索引(下標)+0x016 WaitType : 1 //只等待一個等待塊時置1//等待所有等待對象符合條件時,置0

    一個線程等待多個對象

    實驗

    第一步:編譯并運行以下代碼
    #include <stdio.h> #include <windows.h>HANDLE hEvent[2];DWORD WINAPI ThreadProc(LPVOID lpParamter) {::WaitForMultipleObjects(2, hEvent, FALSE, -1);printf("ThreadProc函數執行\n");return 0; }int main(int argc, char* argv[]) {hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL); //創建可等待對象hEvent[1] = ::CreateEvent(NULL, TRUE, FALSE, NULL); //創建可等待對象::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);getchar();return 0; }
    第二步:在WinDbg中找到該進程

    !process 0 0



    第三步:查看線程信息

    dt _KTHREAD 862b3340


    共有兩個等待塊

    注意:此時WaitType字段的值仍是1,這是因為只要有一個對象滿足條件,當前線程就可以被喚醒(詳見第一步代碼部分)

    第四步:查看等待塊具體細節

    代碼中創建的對象為 _KEVENT,所以 _KWAIT_BLOCK +0x00C Object 指向 _KEVENT +0x000 _DISPATCHER_HEADER

    kd> dt _DISPATCHER_HEADER 0x86015db8 nt_400000!_DISPATCHER_HEADER+0x000 Type : 0 ''+0x001 Absolute : 0x21 '!'+0x002 Size : 0x4 ''+0x003 Inserted : 0x86 ''+0x004 SignalState : 0n0+0x008 WaitListHead : _LIST_ENTRY [ 0x862b33c8 - 0x862b33c8 ]

    WaitListHead:雙向鏈表,圈著所有等待塊的WaitListEntry

    等待網

    總結

  • 等待中的線程,一定在等待鏈表中(KiWaitListHead),同時也一定在這張網上(KTHREAD +5C的位置不為空)
  • 線程通過調用WaitForSingleObject/WaitForMultipleObjects函數將自己掛到這張網上
  • 線程什么時候會再次執行取決于其他線程何時調用相關函數,等待對象不同調用的函數也不同
  • 總結

    以上是生活随笔為你收集整理的Windows事件等待学习笔记(二)—— 线程等待与唤醒的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。