之前的內容介紹了內核中主要的同步物件. 除了使用同步物件, 還有幾種方法可以實現同步. 這裡將對這幾種方法進行簡介.
使用自旋鎖進行同步 :
在驅動程式中, 經常使用自旋鎖作為一種有效的同步機制. 例如在應用程式中打開一個裝置後, 有時候需要該啟多個執行緒去操作裝置 (例如都呼叫 ReadFile 函式對裝置進行讀取操作). 這時後 IRP_MJ_READ 的派遣函式也會同步執行. 但是大部分裝置沒有能力回應同步的讀請求, 必須完成一個讀請求後在完成另一個讀請求. 這時後就須要進行同步處理, 程式設計師可以選擇採用前面介紹的事件, 信號燈, 互斥體等內核物件, 但還有另一種選擇, 這就是使用自旋鎖.
對於要同步的程式碼, 需要用同一把自旋鎖進行同步. 如果程式得到了自旋鎖, 其他程式希望獲取自旋鎖時, 則不停地進入自旋狀態. 獲得自旋鎖的內核函式是 KeAcquireSpinLock. 直到自旋鎖被釋放後, 另外的程式才能獲取到自旋鎖. 釋放自旋鎖的內核函式是 KeReleaseSpinLock. 如果希望同步某段程式碼區域, 需要在這段程式碼區域前獲取自旋鎖, 在程式碼區域後釋放自旋鎖. 在單 CPU 的系統中, 獲取自旋鎖是透過提升 IRQL 實現的, 而在多 CPU 的系統中, 實現方法比較複雜, 有興緒的讀者可以自行研究.
無法獲取自旋鎖的執行緒會不停地自旋, 這會浪費很多 CPU 時間. 因此需要同步的程式碼區域不能過長, 換句話說就是佔有自旋鎖的時間不宜太長. 下面程式碼類比了應用程式新建一個裝置後, 同時開啟多個執行緒對裝置進行請求的情況, 這個例子採用的同步機制就是使用自旋鎖 :
驅動程式的派遣函式需要進行同步處理, 下面是部份代碼 :
使用互鎖操作進行同步 :
在本章開始曾經引出一個例子, 來描述同步處理的必要性. 回憶這個例子, C 語言中變數的遞增語句會被編譯器編譯成一段組合語言指令. 例如下面的程式碼在多執行緒環境中, 就存在 "Race condition" 問題. 語句 number++ 不是執行的最小單位, 最小的執行單位是組合語言指令. 每條組合語言指令都有可能被打斷 :
- int number=0;
- void Foo()
- {
- number++;
- // do something...
- number--;
- }
- int number=0;
- void Foo()
- {
- // 獲取自旋鎖
- KeAcquireSpinLock(...);
- number++;
- // 釋放自旋鎖
- KeReleaseSpinLock(...);
- // do something...
- // 獲取自旋鎖
- KeAcquireSpinLock(...);
- number--;
- // 釋放自旋鎖
- KeReleaseSpinLock(...);
- }
- int number=0;
- void Foo()
- {
- // 原子操作
- InterlockedIncrement(&number);
- // do something
- // 原子操作
- InterlockedDecrement(&number);
- }
沒有留言:
張貼留言