48 Windows驅動程式模型筆記(六),同步

2021-09-06 02:13:33 字數 4323 閱讀 3742

關於

同步<?xml:namespace prefix = o />

執行在某執行緒上下文中的**在任何時刻都可能被系統奪去控制權。另外,只有在多處理器的計算機上才能真正實現多執行緒的併發執行

。windows nt為解決一般的同步問題提供了兩種方法,乙個是中斷請求優先順序(irql)方案,另乙個是在關鍵**段周圍宣告和釋放自旋鎖。

irql可以避免在單 cpu上的破壞性搶先,而自旋鎖可以防止多cpu間的干擾。

示. 中斷請求級

在dispatch_level級和profile_level級之間是各種硬體中斷級。通常,每個有中斷能力的裝置都有乙個irql,它定義了該裝置的 中斷優先級別。wdm驅動程式只有在收到乙個副功能碼為irp_mn_start_device的irp_mj_pnp請求後,才能確定其裝置的 irql。裝置的配置資訊作為引數傳遞給該請求,而裝置的irql就包含在這個配置資訊中。

基本同步規則

遵循下面規則,你可以利用irql的同步效果:

所有對共享資料的訪問都應該在同一(提公升的)irql上進行。

換句話說,不論何時何地,如果你的**訪問的資料物件被其它**共享,那麼你應該使你的**執行在高於passive_level的級上。一旦越過 passive_level級,作業系統將不允許同irql的活動相互搶先,從而防止了潛在的衝突。然而這個規則不足以保護多處理器機器上的資料,在多處理器機器中你還需要另外的防護措施——自旋鎖(spin lock)。如果你僅關心單cpu上的操作,那麼使用irql就可以解決所有同步問題。但事實上,所有wdm驅動程式都必須設計成能夠執行在多處理器的系統上。

irql與執行緒優先順序

執行緒優先順序是與irql非常不同的概念。執行緒優先順序控制著執行緒排程器的排程動作,決定何時搶先執行執行緒以及下一次執行什麼執行緒。然而,

當irql級高於或等於dispatch_level級時執行緒切換停止,無論當前活動的是什麼執行緒都將保持活動狀態直到irql降到dispatch_level級之下。

而此時的「優先順序」僅指irql本身,由它控制到底哪個活動該執行,而不是該切換到哪個執行緒的上下文。

irql和分頁

執行在提公升的irql級上的乙個後果是,系統將不能處理頁故障(系統在apc級處理頁故障)。這意味著:

執行在高於或等於dispatch_level級上的**絕對不能造成頁故障。這也意味著執行在高於或等於dispatch_level級上的**必須存在於非分頁記憶體中。此外,所有這些**要訪問的資料也必須存在於非分頁記憶體中。最後,隨著irql的提公升,你能使用的核心模式支援例程將會越來越少。

自旋鎖關於自旋鎖有兩個明顯的事實。第一,如果乙個已經擁有某個自旋鎖的cpu想第二次獲得這個自旋鎖,則該cpu將死鎖(deadlock)。自旋鎖沒有與其關聯的「使用計數器」或「所有者標識」;鎖或者被占用或者空閒。如果你在鎖被占用時獲取它,你將等待到該鎖被釋放。如果碰巧你的cpu已經擁有了該鎖,那麼用於釋放鎖的**將得不到執行,因為你使cpu永遠處於「測試並設定」某個記憶體變數的自旋狀態。

關於自旋鎖的另乙個事實是,cpu在等待自旋鎖時不做任何有用的工作,僅僅是等待。所以,為了避免影響效能,你應該在擁有自旋鎖時做盡量少的操作,因為此時某個cpu可能正在等待這個自旋鎖。

關於自旋鎖還存在著乙個不太明顯但很重要的事實:你僅能在低於或等於dispatch_level級上請求自旋鎖,在你擁有自旋鎖期間,核心將把你的**提公升到dispatch_level級上執行。在內部,核心能在高於dispatch_level的級上獲取自旋鎖,但你和我都做不到這一點。

核心同步物件

windows nt提供了五種核心同步物件(kernel dispatcher object),你可以用它們控制非任意執行緒(普通執行緒)的流程。

在任何時刻,任何物件都處於兩種狀態中的一種:訊號態或非訊號態。

物件 資料型別

描述 event(

事件)

kevent

阻塞乙個執行緒直到其它執行緒檢測到某事件發生

semaphore(

訊號燈)

ksemaphore

與事件物件相似,但可以滿足任意數量的等待

mutex(

互斥)

kmutex

執行到關鍵**段時,禁止其它執行緒執行該**段

timer(

定時器)

ktimer

推遲執行緒執行一段時期

thread(

執行緒)

kthread

阻塞乙個執行緒直到另乙個執行緒結束

圖示 核心同步物件

關於阻塞:

當我們處理某個請求時,僅能阻塞產生該請求的執行緒。

執行在高於或等於dispatch_level級上的**不能阻塞執行緒。

一般在驅動程式的派遣函式中阻塞當前執行緒。

程式設計中的注意點

1、kewaitforsingleobject中引數alertable是乙個布林型別的值。它不同於waitreason,這個引數以另一種方式影響系統行為,它決定等待是否可以提前終止以提交乙個apc。如果等待發生在使用者模式中,那麼記憶體管理器就可以把執行緒的核心模式堆疊換出。如果驅動程式以自動變數(在堆疊中)形式建立事件物件,並且某個執行緒又在提公升的irql級上呼叫了kesetevent,而此時該事件物件剛好又被換出記憶體,結果將產生乙個bug check。所以我們應該總把alertable引數指定為false,即在核心模式中等待。如果你的**執行在dispatch_level級上,則必須指定0超時,因為在這個irql上不允許阻塞。

2、kewaitformultipleobjects

如果你指定了waitall,則返回值status_success表示等待的所有物件都進入了訊號態。如果你指定了waitany,則返回值在數值上等於進入訊號態的物件在objects陣列中的索引。如果碰巧有多個物件進入了訊號態,則該值僅代表其中的乙個,可能是第乙個也可能是其它。你可以認為該值等於status_wait_0加上陣列索引。你可以先用nt_success測試返回碼,然後再從其中提取陣列索引:

ntstatus status = kewaitformultipleobjects(...);

if (nt_success(status))

正處於自己時間片中的執行緒不能被阻塞。

3、kesetevent的第三個引數,我們總指定這個引數為false。具體原因見[2],p115。

4、通知定時器允許有任意數量的等待執行緒。同步定時器正相反,它只允許有乙個等待執行緒;一旦有執行緒在這種定時器上等待,定時器就自動進入非訊號態。

通過使用定時器的擴充套件設定函式,你可以請求乙個週期性的超時

,這種定時器在第一次倒計時時使用duetime時間,到期後再使用period值重複倒計時。

5、作為乙個通用規則,你絕不要寫同步響應使用者模式請求的**,僅能為確定的i/o控制請求這樣做。一般說來,最好掛起長耗時的操作(從派遣例程中返回status_pending**)而以非同步方式完成。再有,你不要一上來就呼叫等待原語。執行緒阻塞僅適合裝置驅動程式中的某幾個地方使用。

底線是:使用非警惕性等待,除非你知道不這樣做的原因。

5、快速互斥體

核心互斥

快速互斥

可以被單執行緒遞迴獲取

(系統為其維護乙個請求計數器

) 不能被遞迴獲取

速度慢

速度快

所有者只能收到

「特殊的」核心

apc

所有者不能收到任何

apc

所有者不能被換出記憶體

不自動提公升被阻塞執行緒的優先順序

(如果執行在大於或等於

apc_level級)

,除非你使用

***unsafe

函式並且執行在

passive_level

級上 可以是多物件等待的一部分

不能作為

kewaitformultipleobjects

的引數使用

apc-

asynchronous procedure call

如果你擁有快速互斥物件你就不能發出apc,這意味著你將處於apc_level或更高的irql,在這一級上,執行緒優先順序將失效,但你的**將不受干擾地執行,除非有硬體中斷發生。

6、你只能在低於或等於dispatch_level級上呼叫s鍊錶函式。只要所有對鍊錶的引用都使用exinterlocked***函式,那麼訪問雙鏈表和單鏈表的exinterlocked***函式可以在任何irql上呼叫。這些函式沒有irql限制的原因是因為它們在執行時都禁止了中斷,這就等於把irql提公升到最高可能的級別。一旦中斷被禁止,這些函式就獲取你指定的自旋鎖。因為此時在同一cpu上沒有其它**能獲得控制,並且其它cpu上的**也不能獲取那個自旋鎖,所以你的鍊錶是安全的。

參考[2] windows驅動程式模型設計

43 Windows驅動程式模型筆記 一

1 通常,驅動程式在某些不可 執行緒的上下文中應該使用非同步方式處理 i o請求。我們使用術語任意執行緒上下文 arbitrary thread context 來描述驅動程式並不知道 或並不關心 處理器當前執行在哪乙個執行緒上的上下文。2 windows 2000 使用對稱多處理器模型,即所有的處...

windows驅動程式開發初探

最近,由於需求推動,自己得開始學一下在windows下如何開發驅動程式。雖然,後來由於其他的原因使得學習沒繼續下去,但是我還是把一些粗略的學習經歷與體會寫在這裡,方便自己日後捲土重來,也方便其他的對windows開發一竅不通的有志之士參考一下。一 開發的目的 二 開發的歷程 1 學習的歷程 要開發出...

Linux裝置驅動程式模型

第1章 linux裝置驅動程式模型 linux系統中包含字元裝置 塊裝置 網路裝置三類基本的裝置驅動程式。隨著技術的不斷進步,linux驅動程式體系的拓撲結構越來越複雜,linux 2.4核心已經不能適應這種形勢的需求。為此2.6核心開發了全新的裝置模型,它採用sysfs檔案系統,該檔案系統是乙個類...