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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WDK tips (9.1) 同步机制与锁

發布時間:2023/12/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WDK tips (9.1) 同步机制与锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這篇文章基本上就是把WDK文檔復述了一下,算不上原創,各位將就著看吧。

在用戶態的世界很多程序員(特別是*NIX界的人)不喜歡用多線程,認為這東西大大增加了程序的復雜度的同時帶來的好處卻不多,他們寧愿用進程來分割任務。當然這是一種很好的設計原則,我個人也持一模一樣的觀點。但是自從多核被炒熱之后這部分內容越來越受關注,你假裝問題不存在已經不可能了,借用冠希哥的日歪普歌詞說就是:就算忘記你們不可能看不見。而在內核態的世界多線程的傳統由來已久,因為內核部分的地址空間多半是共享的,即使是多進程架構在反映在內核部分也就是不同的線程,并且操作硬件的過程中不被重入也是基本要求。如果你開發過驅動或者玩過內核,那么同步機制和鎖你一定已經看的很多了。

各種成熟的操作系統內核都會提供非常豐富的鎖與同步機制來保證你的代碼和資源不被重入,NT內核也不例外。但我發現驅動(或內核)開發人員常用的鎖卻很少,spin lock算一個,event算第二個,了不起加上個mutex就完了,其他鎖的出鏡率低的可憐。我看過比較離譜的例子是只要用到鎖的地方就用spin lock,不管合適不合適。這么做想也知道是不對的,操作系統提供了不同類型的鎖自然是希望你不同情況用不同的,全用spin lock雖然沒風險但顯然也很低效。本節列舉了WDK中提供的各種鎖機制,并試圖指出何時該用哪種鎖,以及更重要的,何時不該用那種。先從自旋鎖說起。

自旋鎖(spin lock)

自旋鎖基本上算是最簡單的同步機制了。我記得上學那會兒剛會寫程序的時候碰到要同步的情況會寫類似下面的代碼:

while( i == 0 );i = 0// do something i = 1

寫完上面一段代碼只需要幾分鐘,debug卻debug了一天,弄的自己灰頭土臉,被“高手”看了后免不了的又會被羞辱一通。沒有一個靠譜的程序員會這么做同步,但是不管你信不信,自旋鎖的核心機制就是上面那段while循環。所謂的自旋,就是CPU在某條指令上不停的回繞直到條件成立跳出,也就是我們常說的忙等。那上面那段代碼也是不停自旋,它有什么問題?我們說它在多線程環境下會出問題。假設上面那兩句編譯完了后變成如下指令:

WAIT:test if i equals 0jump to WAIT if trueset i into 0// do somethingset i into 1

又假設當前 i 為1,我們有兩條線程同時執行,產生的指令流如下:

時間線程1線程2
0test if i not 0?
1?test if i not 0
2set i into 0?
3?set i into 0
4

第0條和第1條指令執行完后,兩條線程都進入了臨界區,很顯然這是一個bug。這里的問題是test指令和set指令是分開的,中間會被重入,而我們不希望這種重入的發生。一個正確的實現要么需要及其復雜的算法保證,要么需要CPU提供set-and-test指令,在一條指令里面做完test和set兩件事,保證不會被重入。NT內核用的是set-and-test指令,事實上這種指令在用戶態也可以使用,就是InterlockedXXX那一堆API.

在多核CPU架構里set-and-test指令往往會把bus鎖住導致并行效率降低,為了減少此類事件的發生NT內核實現自旋鎖時用了兩個循環,大循環用set-and-test檢測,如果檢測失敗(也就是獲得鎖失敗)立即進入小循環用普通的test指令檢測,大致的代碼如下:

while( test i not 0 and then set i into 0 ){while( test i not 0 );}

另外考慮到檢測值 i 不是cpu-local的值,兩邊的cpu cache需要不停的同步,每次i修改后參與排隊的cpu的緩存就會變得無效,如果排隊自旋鎖競爭比較激烈的話,頻繁的緩存同步操作會導致繁重的系統總線和內存的流量,從而大大降低了系統整體的性能。又由于各cpu獲得spin lock的時機是無序的,在競爭激烈的情況下很有可能會出現“饑餓”現象。基于這些理由,某些操作系統設計了 一種每個cpu都在自己的local變量上自旋,并能保證按先來先得順序獲得鎖的的算法,此種自旋鎖稱為queued spinlock,具體信息請查看IBM Developerworks 上的文章,MSDN上也有簡單的描述。

WDK里的自旋鎖

之前我們說過NT內核里有IRQL的概念,在APC 及以上的level線程調度是停止的,每個cpu都只可能有一條線程在運行。基于這條原則我們可以很容易的看出,在單核系統里自旋鎖完全沒必要真去自旋,它只需要把IRQL提高到沒有線程切換的等級即可,這就是NT內核做的。每次獲得自旋鎖的時候,內核會把當前的IRQL提高到DISPATCH_LEVEL(或以上)并保存之前的level,然后根據處理器數量做不同的事情:如果是單核,則啥也不做;如果是多核,進入自旋的邏輯。釋放鎖之后則作相反的事:把IRQL設置成之前的level。

WDK提供了三種類型的自旋鎖:普通自旋鎖,普通ISR自旋鎖,以及ISR同步自旋鎖。普通自旋鎖會把IRQL設成DISPATCH_LEVEL,而ISR的IRQL一定會大于DISPATCH_LEVEL,所以如果你在ISR里用普通自旋鎖那么一定會發生BSOD。普通自旋鎖使用前必須要先調用KeInitializeSpinLock進行初始化。普通自旋鎖的獲取用KeAcquireSpinLock函數,釋放用KeReleaseSpinLock。這兩個函數都會修改IRQL,如果你很確認當前的IRQL是DISPATCH_LEVEL,那么可以使用KeAcquireSpinLockAtDpcLevel和KeReleaseSpinLockFromDpcLevel函數,這兩個函數不會修改IRQL。普通ISR自旋鎖允許運行在DIRQL中的ISR和運行在DISPATCH_LEVEL的DPC之間進行同步,ISR同步自旋鎖則是允許不同的ISR之間進行同步。當你調用IoConnectInterrupt時有一個optional parameter叫做SpinLock,如果你傳入的值是NULL,那么IoManager會自動給你生成一個普通自旋鎖,ISR調用前自動加鎖,調用后自動解鎖;如果傳入的值非0(也就是由你自己制定spin lock),那么該spin lock為ISR同步自旋鎖,它可以為不同DIRQL上的ISR提供同步機制。值得注意的是,不管是普通ISR自旋鎖還是ISR同步自旋鎖你都不能用KeAcquireSpinLock加鎖,因為這個函數會把IRQL置為DISPATCH_LEVEL。你應該使用KeSynchronizeExecution進行同步。

與Linux內核里的MCS spin lock類似的,NT內核里也有排隊自旋鎖,而且據我所知,NT內核比Linux更早實現排隊自旋鎖。排隊自旋鎖的工作方式如下:每個處理器上的執行線程都有一個本地的標志,通過該標志,所有使用該鎖的處理器(鎖擁有者和等待者)被組織成一個單向隊列。當一個處理器想要獲得一個已被其它處理器持有的排隊自旋鎖時,它把自己的標志放在該 Queued Spinlock 的單向隊列的末尾。如果當前鎖持有者釋放了自旋鎖,則它將該鎖移交到隊列中位于自己之后的第一個處理器。同時,如果一個處理器正在忙等待排隊自旋鎖,它并不是檢查該鎖自身的狀態,而是檢查針對自己的標志;在隊列中位于該處理器之前的處理器釋放自旋鎖時會設置這一標志,以表明輪到這個正在等待的處理器了。獲取排隊自旋鎖的函數是KeAcquireInStackQueuedSpinLockAtDpcLevel,釋放鎖用KeReleaseInStackQueuedSpinLockFromDpcLevel。照著WDK文檔的說法,給xp以后的操作系統寫驅動都應該用排隊自旋鎖而不是舊的自旋鎖。

死鎖

死鎖發生的條件想必大家都很清楚,避免方法我也沒必要多說,之所以在這里還要提這個話題是因為自旋鎖有一個很奇葩的特性:即使只有一個線程一把鎖,它照樣會死鎖。為方便起見我們假設自旋鎖的實現是這樣的:

void lock(){//……// (1)while( test i not 0 and then set i into 0 ){while( test i not 0 );}// (2) }

另有一個遞歸函數會調用lock()函數,函數返回后釋放鎖,這時會發生什么事?我們看到第一次lock()函數運行到(2)時 i 是一定是0,接著執行第二次lock(),那么那段set-and-test代碼一定沒法跳出,函數不會返回,鎖也就永遠不會釋放。雖然只有一個線程一把鎖,卻也出現了典型的死鎖癥狀。這是使用自旋鎖時特有的問題,其他鎖都不會有這種情況。

轉載于:https://www.cnblogs.com/gussing/archive/2012/06/11/2544475.html

總結

以上是生活随笔為你收集整理的WDK tips (9.1) 同步机制与锁的全部內容,希望文章能夠幫你解決所遇到的問題。

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