程式扎記: [ Windows DDP ] 計時器 : 等待

標籤

2011年3月10日 星期四

[ Windows DDP ] 計時器 : 等待


前言 :
等待是驅動程式中經常用到的, 這裡將 DDK 等待的方法彙總. 總共有四種辦法, 讀者可以根據自己的需要進行選擇.

第一種方法 : 使用 KeWaitForSingleObject
第一種方法是使用 KeWaitForSingleObject 函式. 該函式在前面已經介紹過, 其主要用來等待內核同步對像, 但是也可以用來等待一段時間.
首先初始化一個內核同步物件, 其初始化狀態為未觸發狀態. 然後呼叫 KeWaitForSingleObject 並對其設置 timeout 參數, 該參數是需要等待的時間. 下面程式碼演示了如何用 KeWaitForSingleObject 等待一段時間.
- 範例代碼 (1) : 使用 KeWaitForSingleObject 等待
  1. VOID WaitMicroSecond1(ULONG ulMircoSecond)  
  2. {  
  3.     KEVENT kEvent;  
  4.   
  5.     KdPrint(("Thread suspends %d MircoSeconds...",ulMircoSecond));  
  6.   
  7.     //初始化一個未激發的內核事件  
  8.     KeInitializeEvent(&kEvent,SynchronizationEvent,FALSE);  
  9.   
  10.     //等待時間的單位是100納秒,將微秒轉換成這個單位  
  11.     //負數代表是從此刻到未來的某個時刻  
  12.     LARGE_INTEGER timeout = RtlConvertLongToLargeInteger(-10*ulMircoSecond);  
  13.   
  14.     //在經過timeout後,執行緒繼續執行  
  15.     KeWaitForSingleObject(&kEvent,  
  16.         Executive,  
  17.         KernelMode,  
  18.         FALSE,  
  19.         &timeout);  
  20.   
  21.     KdPrint(("Thread is running again!\n"));  
  22. }  

第二種方法 : 使用 KeDelayExecutionThread
第二種方法是使用內核函式 KeDelayExecutionThread, 該內核函式和 KeWaitForSingleObject 類似, 都是強制當前執行緒進入睡眠狀態. 經過指定時間後, 執行時間恢復執行. 下面程式碼演示了如何使用 KeDelayExecutionThread 等待一段時間.
- 範例代碼 (2) : 使用 KeDelayExecutionThread 等待
  1. VOID WaitMicroSecond2(ULONG ulMircoSecond)  
  2. {  
  3.     KdPrint(("Thread suspends %d MircoSeconds...",ulMircoSecond));  
  4.   
  5.     //等待時間的單位是100納秒,將微秒轉換成這個單位  
  6.     //負數代表是從此刻到未來的某個時刻  
  7.     LARGE_INTEGER timeout = RtlConvertLongToLargeInteger(-10*ulMircoSecond);  
  8.   
  9.     //此種方法類似於KeWaitForSingleObject  
  10.     //將當前執行緒進入睡眠狀態,間隔時間到轉入執行狀態  
  11.     KeDelayExecutionThread(KernelMode,FALSE,&timeout);  
  12.   
  13.     KdPrint(("Thread is running again!\n"));  
  14. }  

第三種方法 : 使用 KeStallExecutionProcessor
第三種方法是使用內核函式 KeStallExecutionProcessor該內核函式是讓 CPU 處於忙碌等候狀態, 而不是處於睡眠. 經過指定時間後, 繼續讓執行緒執行.
這種方法是讓 CPU 不停地等待, 而不是讓執行緒進入休眠, 類似於自旋鎖. 因此這種方法浪費 CPU 時間. DDK 文檔規定 KeStallExecutionProcessor 不宜超過 50us. 由於沒有執行緒進入睡眠, 也不會發生執行緒的切換, 因此這種方法的延時比較精確. 下面程式碼演示了如何使用 KeStallExecutionProcessor 等待一段時間 :
- 範例代碼 (3) : 使用 KeStallExecutionProcessor 等待
  1. VOID WaitMicroSecond3(ULONG ulMircoSecond)  
  2. {  
  3.     KdPrint(("Thread suspends %d MircoSeconds...",ulMircoSecond));  
  4.   
  5.     //忙等待,此種方法屬於忙等待,比較浪費CPU時間  
  6.     //因此使用該方法不宜超過50微秒  
  7.     KeStallExecutionProcessor(ulMircoSecond);  
  8.   
  9.     KdPrint(("Thread is running again!\n"));  
  10. }  

第四種方法 : 使用計時器
第四種方法是使用計時器物件, 注意這和前面講的 DPC 計時器略有不同. 這裡沒有用到 DPC 物件與 DPC 常式, 因此當指定時間到後, 不會進入 DPC 常式.
計時器物件和其他內核同步物件一樣, 也式有兩個狀態, 一個是未激發狀態, 一個是激發狀態. 在初始化計時器時, 計時器處於未激發狀態. 當使用 KeSetTimer 後, 經過指定時間, 進入激發狀態. 這樣就可以使用 KeWaitForSingleObject 函式對計時器進行等待, 以下是一段範例代碼 :
- 範例代碼 (4) : 使用計時器等待
  1. VOID WaitMicroSecond4(ULONG ulMircoSecond)  
  2. {  
  3.     //使用計時器  
  4.   
  5.     KTIMER kTimer;//內核計時器  
  6.   
  7.     //初始化計時器  
  8.     KeInitializeTimer(&kTimer);  
  9.   
  10.     LARGE_INTEGER timeout = RtlConvertLongToLargeInteger( ulMircoSecond * -10 );  
  11.   
  12.     //注意這個計時器沒有和DPC物件關聯  
  13.     KeSetTimer(&kTimer,timeout, NULL);  
  14.     KdPrint(("Thread suspends %d MircoSeconds...",ulMircoSecond));  
  15.   
  16.     KeWaitForSingleObject(&kTimer,Executive,KernelMode,FALSE,NULL);  
  17.   
  18.     KdPrint(("Thread is running again!\n"));  
  19. }  
This message was edited 4 times. Last update was at 27/01/2011 12:07:56

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!