亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

? 歡迎來到蟲蟲下載站! | ?? 資源下載 ?? 資源專輯 ?? 關于我們
? 蟲蟲下載站

?? 漫談兼容內核之十五:windows線程的等待、喚醒機制.txt

?? 漫談系統內核內幕 收集得很辛苦 呵呵 大家快下在吧
?? TXT
?? 第 1 頁 / 共 3 頁
字號:
漫談兼容內核之十五:Windows線程的等待/喚醒機制

[align=center] 毛德操[/align]

    對于任何一個現代的操作系統,進程間通信都是不可或缺的。
    共享內存區顯然可以用作進程間通信的手段。兩個進程把同一組物理內存頁面分別映射到各自的用戶空間,然后一個進程往里面寫,另一個進程就可以讀到所寫入的內容。所以,共享內存區天然就是一種進程間通信機制。但是這又是很原始的手段,因為這里有個讀出方如何知道共享區的內容已經被寫入方改變的問題。輪詢,或者定期輪詢,當然也是個辦法,但是一般而言效率畢竟太低。所以,這里需要有個能夠對通信雙方的活動加以有效協調的機制,這就是“進程間同步”機制。進程間同步本身也是一種進程間通信(因為涉及信息的交換),當然也是一種原始的進程間通信,但同時又是更高級的進程間通信機制的基石。
    所以,在談論通信機制之前,應該先考察一下進程間同步機制。在Linux中,這就是進程的睡眠/喚醒機制,或者說阻塞/解阻塞機制,體現為信息的接收方(進程)在需要讀取信息、而發送方(進程)尚未向其發送之時就進入睡眠,到發送方向其發送信息時則加以喚醒。在Windows中,這個過程的原理是一樣的,只是名稱略有不同,稱為“等待/喚醒”,表現形式上也有些不同。

    在Windows中,進程間通信必須憑籍著某個已打開的“對象(Object)”才能發生(其實Linux中也是一樣,只是沒有統一到“對象”這個概念上)。我們不妨把這樣的對象想像成某類貨品的倉庫,信息的接受方試圖向這個倉庫領貨。如果已經到貨,那當然可以提了就走,但要是尚未到貨就只好等待,到一邊歇著去(睡眠),直至到了貨才把它(喚醒)叫回來提貨。Windows專門為此過程提供了兩個系統調用,一個是NtWaitForSingleObject(),另一個是NtWaitForMultipleObjects()。后者是前者的推廣、擴充,使得一個線程可以同時在多個對象上等待。
    于是,在Windows應用程序中,當一個線程需要從某個對象“提貨”、即獲取信息時,就通過系統調用NtWaitForSingleObject()實現在目標對象上的等待,當前線程因此而被“阻塞”、即進入睡眠狀態,直至所等待的條件得到滿足時才被喚醒。

[code]NTSTATUS  STDCALL
NtWaitForSingleObject(IN HANDLE ObjectHandle,
        IN BOOLEAN Alertable,
        IN PLARGE_INTEGER TimeOut  OPTIONAL)
{
   . . . . . .
   PreviousMode = ExGetPreviousMode();
  
   if(TimeOut != NULL && PreviousMode != KernelMode)
   {
     _SEH_TRY
     {
       ProbeForRead(TimeOut, sizeof(LARGE_INTEGER), sizeof(ULONG));
       /* make a copy on the stack */
       SafeTimeOut = *TimeOut;
       TimeOut = &SafeTimeOut;
     }
     _SEH_HANDLE
     {
       Status = _SEH_GetExceptionCode();
     }
     _SEH_END;
    
     if(!NT_SUCCESS(Status))
     {
       return Status;
     }
   }

   Status = ObReferenceObjectByHandle(ObjectHandle, SYNCHRONIZE, NULL,
                                          PreviousMode, &ObjectPtr, NULL);
   . . . . . .
   if (!KiIsObjectWaitable(ObjectPtr))
     {
       DPRINT1("Waiting for object type '%wZ' is not supported\n",
        &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
       Status = STATUS_HANDLE_NOT_WAITABLE;
     }
   else
     {
       Status = KeWaitForSingleObject(ObjectPtr, UserRequest,
                              PreviousMode, Alertable, TimeOut);
     }

   ObDereferenceObject(ObjectPtr);
   return(Status);
}[/code]
    參數ObjectHandle和TimeOut的作用不言自明。另一個參數Alertable是個布爾量,表示是否允許本次等待因用戶空間APC而中斷,或者說被“警醒”。警醒與喚醒是不同的,喚醒是因為所等待的條件得到了滿足(倉庫到了貨),而警醒是因為別的原因(與倉庫無關)。
    我們知道,Windows的系統調用函數既可以從用戶空間通過自陷指令int 0x2e加以調用,也可以在內核中直接加以調用。如果是從用戶空間調用,而且又有以指針形式傳遞的參數,那就需要從用戶空間讀取這些指針所指的內容。但是,這些指針所指處的(虛存)頁面是否有映射呢?這是沒有保證的。如果沒有映射,那么在訪問時就會發生“頁面錯誤”異常。另一方面,既然讀不到調用參數,原定的操作也就無法繼續下去了。為此,代碼中把對于目標是否可讀的測試ProbeForRead()以及參數內容的復制放在_SEH_TRY{}中,并且設置好“頁面錯誤”異常處理的向量,使得一旦發生“頁面錯誤”異常就執行_SEH_HANDLE{}中的操作。這是Windows的“結構化出錯處理”即SHE機制的一部分,以后還要有專文介紹。由于篇幅的關系,以后在系統調用的程序中就不再列出這些代碼了。
    NtWaitForSingleObject()中實質性的操作只有兩個。一是ObReferenceObjectByHandle(),就是通過已打開對象的Handle獲取指向該目標對象(數據結構)的指針。第二個操作就是KeWaitForSingleObject(),這是下面要講的。不過,并非對于所有的對象都可以執行這個函數,有的對象是“可等待”的,有的對象卻是“不可等待”的,所以先要通過一個函數KiIsObjectWaitable()加以檢驗。這樣,一言以蔽之,NtWaitForSingleObject()的作用就是對可等待目標對象的數據結構執行KeWaitForSingleObject()。
    那么什么樣的對象才是可等待的呢?看一下這個函數的代碼就知道了:

[code]BOOL inline FASTCALL  KiIsObjectWaitable(PVOID Object)
{
    POBJECT_HEADER Header;
    Header = BODY_TO_HEADER(Object);
   
    if (Header->ObjectType == ExEventObjectType ||
      Header->ObjectType == ExIoCompletionType ||
      Header->ObjectType == ExMutantObjectType ||
      Header->ObjectType == ExSemaphoreObjectType ||
      Header->ObjectType == ExTimerType ||
      Header->ObjectType == PsProcessType ||
      Header->ObjectType == PsThreadType ||
      Header->ObjectType == IoFileObjectType) {
        return TRUE;
    } else {
        return FALSE;
    }
}[/code]

    可見,所謂“可等待”的對象包括進程、線程、Timer、文件,以及用于進程間通信的對象Event、Mutant、Semaphore,還有用于設備驅動的IoCompletion。這IoCompletion屬于設備驅動框架,所以KeWaitForSingleObject()既是進程間通信的重要一環,同時也是設備驅動框架的一個重要組成部分。
    注意這里(取自ReactOS)關于對象數據結構的處理是很容易讓人摸不著頭腦的,因而需要加一些說明。首先,每個進程的“打開對象表”是由Handle表項構成的,是一個HANDLE_TABLE_ENTRY結構指針數組。而HANDLE_TABLE_ENTRY數據結構中有個指針指向另一個數據結構(而且這個指針的低位又被用于一些標志位),可是這個數據結構并非具體對象的數據結構,而是一個通用的OBJECT_HEADER數據結構:

[code]typedef struct _OBJECT_HEADER
/*
* PURPOSE: Header for every object managed by the object manager
*/
{
   UNICODE_STRING Name;
   LIST_ENTRY Entry;
   LONG RefCount;
   LONG HandleCount;
   BOOLEAN Permanent;
   BOOLEAN Inherit;
   struct _DIRECTORY_OBJECT* Parent;
   POBJECT_TYPE ObjectType;
   PSECURITY_DESCRIPTOR SecurityDescriptor;
  
   /*
    * PURPOSE: Object type
    * NOTE: This overlaps the first member of the object body
    */
   CSHORT Type;
  
   /*
    * PURPOSE: Object size
    * NOTE: This overlaps the second member of the object body
    */
   CSHORT Size; 
} OBJECT_HEADER, *POBJECT_HEADER;[/code]

    緊隨在OBJECT_HEADER后面的才是具體對象的數據結構的正身、即Body。所以OBJECT_HEADER和Body合在一起才構成一個對象的完整的數據結構。但是,當傳遞一個對象的數據結構指針時,所傳遞的指針卻既不是指向其正身,又不是指向其OBJECT_HEADER,而是指向其OBJECT_HEADER結構中的字段Type。宏定義HEADER_TO_BODY說明了這一點:

[code]#define HEADER_TO_BODY(objhdr)  \
  (PVOID)((ULONG_PTR)objhdr + sizeof(OBJECT_HEADER) \
- sizeof(COMMON_BODY_HEADER))[/code]

    就是說,具體對象數據結構的起點是objhdr加上OBJECT_HEADER的大小、再減去COMMON_BODY_HEADER的大小。而COMMON_BODY_HEADER定義為:

[code]typedef struct
{
   CSHORT Type;
   CSHORT Size;
} COMMON_BODY_HEADER, *PCOMMON_BODY_HEADER;[/code]
    顯然這就是OBJECT_HEADER中的最后兩個字段。那么具體對象的數據結構又是什么樣的呢?我們以Semaphore的數據結構KSEMAPHORE為例:

[code]typedef struct _KSEMAPHORE {
    DISPATCHER_HEADER Header;
    LONG Limit;
} KSEMAPHORE;[/code]

    它的第一個成分是一個DISPATCHER_HEADER數據結構,但是卻看不到Type和Size這兩個字段,也看不到COMMON_BODY_HEADER。我們進一步看DISPATCHER_HEADER的定義:

[code]typedef struct _DISPATCHER_HEADER {
  UCHAR  Type;
  UCHAR  Absolute;
  UCHAR  Size;
  UCHAR  Inserted;
  LONG  SignalState;
  LIST_ENTRY  WaitListHead;
} DISPATCHER_HEADER, *PDISPATCHER_HEADER;[/code]

    與COMMON_BODY_HEADER相比較,我們確實看到這里有Type和Size,但是中間卻又夾著別的字段。但是,仔細觀察,就可看出在COMMON_BODY_HEADER中Type的類型是16位的CSHORT,而在這里是8位的UCHAR,而且下面Absolute的類型也是UCHAR。這就清楚了,原來COMMON_BODY_HEADER中(以及OBJECT_HEADER中)的Type雖然是16位的,實際上卻只用了其低8位,而在DISPATCHER_HEADER中則將其高8位用作Absolute。編譯器在分配空間時是由低到高(地址)分配的,所以Type是低8位而Absolute是高8位。同樣的道理也適用于Size和Inserted。這樣的安排當然使代碼的可讀性變得很差,筆者尚不明白為什么非得要這么干。另一方面,不同的對象有不同的數據結構,所以代碼中有關對象指針的類型一般總是PVOID,這似乎也合理。但是既然第一個成分總是DISPATCHER_HEADER,那為什么不用PDISPATCHER_HEADER呢?那樣至少也可以改善一些可讀性。
    回到NtWaitForSingleObject()的代碼,我們需要進一步往下看KeWaitForSingleObject()的代碼。不過在此之前先得考察一下有關的數據結構。
    首先,執行這個函數的主體是個線程,而所等待的又是通過一個對象傳遞的信息,就一定要有個數據結構把這二者連系起來,這就是KWAIT_BLOCK數據結構:

[code]typedef struct _KWAIT_BLOCK
/*
* PURPOSE: Object describing the wait a thread is currently performing
*/
{
   LIST_ENTRY WaitListEntry;
   struct _KTHREAD* Thread;
   struct _DISPATCHER_HEADER *Object;
   struct _KWAIT_BLOCK* NextWaitBlock;
   USHORT WaitKey;
   USHORT WaitType;
} KWAIT_BLOCK, *PKWAIT_BLOCK;[/code]
    這里的Thread和Object都是指針。前者指向一個KTHREAD數據結構,代表著正在等待的線程;后者指向一個對象的數據結構,雖然指針的類型是DISPATCHER_HEADER*,但是如上所述這是不管什么對象的數據結構中的第一個成分,所以指向這個數據結構也就是指向了它所在對象的數據結構。此外,結構中的成分WaitListEntry顯然是用來把這個數據結構掛入某個(雙鏈)隊列的,同時指針NextWaitBlock也是用來維持一個(單鏈)隊列。這是因為一個“等待塊”即KWAIT_BLOCK數據結構可能同時出現在兩個隊列中。首先,多個線程可能在同一個對象上等待,每個線程為此都有一個等待塊,從而形成特定目標對象的等待隊列,這就是由WaitListEntry維持的隊列。這樣,對于一個具體的對象而言,其等待隊列中的每個等待塊都代表著一個線程。同時,一個線程又可能同時在多個對象上等待,因而又可能有多個等待塊。對于這個線程而言,每個等待塊都代表著一個不同的對象,這些等待塊則通過NextWaitBlock構成一個隊列。其余字段的作用以后就會明白。
    既然等待是具體線程的行為,線程數據結構中就得有相應的安排,KTHREAD結構中與此有關的成分如下:

[code]typedef struct _KTHREAD
{
   /* For waiting on thread exit */
   DISPATCHER_HEADER DispatcherHeader;    /* 00 */
   . . . . . .
   LONG              WaitStatus;           /* 50 */
   KIRQL             WaitIrql;              /* 54 */
   CHAR              WaitMode;            /* 55 */
   UCHAR            WaitNext;            /* 56 */
   UCHAR            WaitReason;           /* 57 */
   PKWAIT_BLOCK    WaitBlockList;        /* 58 */
   LIST_ENTRY        WaitListEntry;         /* 5C */
   ULONG             WaitTime;            /* 64 */
   CHAR              BasePriority;          /* 68 */
   UCHAR             DecrementCount;      /* 69 */
   UCHAR             PriorityDecrement;     /* 6A */
   CHAR              Quantum;             /* 6B */
   KWAIT_BLOCK      WaitBlock[4];        /* 6C */
   PVOID              LegoData;            /* CC */
   . . . . . .
  
} KTHREAD;[/code]

    首先我們注意到這里有個結構數組WaitBlock[4],這就是KWAIT_BLOCK數據結構座落所在,需要時就在這里就地取材。之所以是個數組,是因為有時候需要同時在多個對象上等待,這就是KeWaitForMultipleObjects()的目的,有點類似于Linux中的select()。此時KWAIT_BLOCK指針WaitBlockList指向本線程的等待塊隊列。如前所述,這個隊列中的每個等待塊都代表著一個對象。WaitStatus則是狀態信息,在結束等待時反映著結束的原因。
    下面我們就來看KeWaitForSingleObject()的代碼。

[code][NtWaitForSingleObject() > KeWaitForSingleObject()]

NTSTATUS STDCALL
KeWaitForSingleObject(PVOID Object,
                      KWAIT_REASON WaitReason,
                      KPROCESSOR_MODE WaitMode,
                      BOOLEAN Alertable,
                      PLARGE_INTEGER Timeout)
{
    PDISPATCHER_HEADER CurrentObject;
    PKWAIT_BLOCK WaitBlock;
    PKWAIT_BLOCK TimerWaitBlock;
    PKTIMER ThreadTimer;
    PKTHREAD CurrentThread = KeGetCurrentThread();
    NTSTATUS Status;
    NTSTATUS WaitStatus;

    . . . . . .
    /* Check if the lock is already held */
    if (CurrentThread->WaitNext)  {

?? 快捷鍵說明

復制代碼 Ctrl + C
搜索代碼 Ctrl + F
全屏模式 F11
切換主題 Ctrl + Shift + D
顯示快捷鍵 ?
增大字號 Ctrl + =
減小字號 Ctrl + -
亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
国产在线看一区| 91精品国产一区二区三区| 色偷偷久久一区二区三区| 日韩欧美一区二区在线视频| 成人欧美一区二区三区白人 | 亚洲最大成人综合| 国模无码大尺度一区二区三区| 一本大道av一区二区在线播放| 久久久久国产精品免费免费搜索| 亚洲国产成人porn| 欧美日韩精品福利| 亚洲色图欧洲色图婷婷| 国产盗摄精品一区二区三区在线| 这里只有精品免费| 性做久久久久久免费观看欧美| 91无套直看片红桃| 亚洲国产精品黑人久久久| 国产在线不卡视频| 久久色在线视频| 精品在线播放免费| 欧美成人一区二区| 韩国一区二区视频| 精品少妇一区二区三区免费观看| 五月天激情小说综合| 欧美日韩国产一级片| 伊人一区二区三区| 在线观看av一区二区| 亚洲一区电影777| 欧美色精品天天在线观看视频| 一区二区三区资源| 欧美日韩免费视频| 午夜精品一区二区三区免费视频| 欧美午夜片在线看| 日韩精品欧美精品| 精品国产一区二区三区久久影院| 久久狠狠亚洲综合| 国产女人水真多18毛片18精品视频| 国产精品99久久久久| 久久精品日产第一区二区三区高清版 | 欧美精品自拍偷拍动漫精品| 日韩电影免费一区| 日韩精品一区二区三区在线观看 | 亚洲成人你懂的| 91.com在线观看| 老鸭窝一区二区久久精品| 亚洲精品在线网站| 成人综合婷婷国产精品久久| 国产精品美女久久福利网站| 91视频观看视频| 日韩精品一区第一页| 久久免费电影网| 91亚洲大成网污www| 视频一区免费在线观看| 久久久久久久精| 91丨九色丨尤物| 日本成人在线电影网| 中文字幕欧美国产| 欧美三电影在线| 国产精品一区二区久久精品爱涩| 综合中文字幕亚洲| 91精品国产综合久久久久久久| 国产精品一区免费视频| 亚洲人成在线播放网站岛国| 欧美人xxxx| 成人av在线网站| 奇米一区二区三区av| 中文字幕av一区 二区| 欧美视频完全免费看| 日本电影欧美片| 美脚の诱脚舐め脚责91| 国产精品久久久久9999吃药| 欧美日韩国产高清一区二区| 国产一区二区三区在线观看免费 | 国产精品亚洲第一| 一区二区不卡在线视频 午夜欧美不卡在 | 亚洲国产欧美另类丝袜| 久久综合av免费| 欧美视频完全免费看| 成人一区二区三区视频在线观看| 亚洲一区二区在线视频| 欧美韩日一区二区三区四区| 91精品在线一区二区| 91亚洲精品久久久蜜桃| 国产精品一区二区三区乱码| 亚洲高清在线精品| 综合久久久久综合| 国产婷婷色一区二区三区在线| 欧美精品在线视频| 99精品视频一区二区三区| 美国毛片一区二区| 亚洲国产日韩在线一区模特| 国产精品久久久久影视| 久久久久久夜精品精品免费| 欧美精品日韩一本| 欧美视频在线播放| 一本一道久久a久久精品综合蜜臀| 国产一区亚洲一区| 久久99精品国产.久久久久久| 一区二区三区高清| 亚洲丝袜另类动漫二区| 国产精品三级电影| 中文一区二区在线观看| 国产午夜精品久久久久久久| 精品日韩一区二区三区免费视频| 欧美日韩一二三区| 欧美性高清videossexo| 在线一区二区三区四区| 99精品1区2区| 91免费观看国产| 91在线你懂得| 91在线一区二区| 色婷婷激情综合| 91国内精品野花午夜精品| 91网上在线视频| 在线免费观看日本欧美| 91黄色小视频| 欧美久久久久久久久久| 4438成人网| 精品粉嫩超白一线天av| 久久久久久亚洲综合| 日本一区二区三区四区在线视频 | 日韩网站在线看片你懂的| 欧美一区二区三区四区在线观看| 欧美精品xxxxbbbb| 91精品国产综合久久香蕉的特点| 91精品国产综合久久精品麻豆| 欧美一区日本一区韩国一区| 这里只有精品电影| 久久婷婷一区二区三区| 中文在线一区二区| 亚洲黄色尤物视频| 国产精品18久久久久久久网站| 狠狠色丁香久久婷婷综合_中 | 久久午夜国产精品| 亚洲人成影院在线观看| 亚洲综合久久av| 欧美aaa在线| 成人深夜视频在线观看| 欧美怡红院视频| 欧美xfplay| 亚洲欧美日韩中文播放| 视频一区视频二区中文| 国产精品一区二区在线观看网站 | 亚洲国产视频直播| 久久99深爱久久99精品| 大白屁股一区二区视频| 欧美性猛交一区二区三区精品| 欧美一区二区高清| 国产精品久久综合| 日韩主播视频在线| 懂色av噜噜一区二区三区av| 欧美日精品一区视频| 久久久国产精品午夜一区ai换脸| 亚洲精品视频在线| 久久er99精品| 欧美性生活久久| 欧美激情一区二区三区在线| 亚洲国产一区在线观看| 国产一区二区不卡在线| 91成人在线免费观看| 久久久久久99久久久精品网站| 亚洲久本草在线中文字幕| 国产永久精品大片wwwapp| 欧美色欧美亚洲另类二区| 中文字幕精品一区二区精品绿巨人| 亚洲国产精品久久艾草纯爱| 成人一区二区三区视频在线观看 | 狠狠色狠狠色综合| 欧美日韩视频第一区| 国产精品天干天干在线综合| 另类小说视频一区二区| 日本精品一级二级| 国产精品国产三级国产有无不卡| 老司机午夜精品99久久| 在线观看精品一区| 中文字幕一区二区三区在线播放| 日本成人在线网站| 欧美日韩视频一区二区| 亚洲人成影院在线观看| 国产成人精品免费一区二区| 欧美videos大乳护士334| 三级久久三级久久久| 91啪亚洲精品| 亚洲日本在线天堂| 99久久99久久综合| 国产精品久久久爽爽爽麻豆色哟哟 | 精品1区2区在线观看| 三级精品在线观看| 欧美精品乱码久久久久久按摩 | 色哟哟日韩精品| 1024精品合集| 99久久精品一区| 国产精品美女久久久久久久久| 国产成人精品一区二| 精品第一国产综合精品aⅴ| 麻豆成人av在线| 精品欧美一区二区在线观看| 日韩av一区二| 精品国产91洋老外米糕| 国产综合色在线|