架構型驅動程式中的所有程式代碼幾乎都位於事件回呼函式中。 架構會自動同步處理驅動程式的大部分回呼函式,如下所示:
架構一律會同步處理 一般裝置物件、 功能性裝置物件 (FDO)和 實體裝置物件 (PDO) 事件回呼函式。 每個裝置一次只能呼叫其中一個回呼函式。 例外狀況是 EvtDeviceSurpriseRemoval、 EvtDeviceQueryRemove 和 EvtDeviceQueryStop。 這些回呼函數支援隨插即用(PnP)和電源管理事件,且會在 IRQL = PASSIVE_LEVEL 呼叫。
架構可以選擇同步驅動程式的 I/O 要求回呼函式的執行,讓這些回呼函式按順序逐一執行。 具體而言,架構可以同步 處理佇列、 中斷、 延遲過程調用 (DPC)、 定時器、 工作專案和 檔案 物件的回呼函式,以及要求物件的 EvtRequestCancel 回呼函式。 架構會在 IRQL = DISPATCH_LEVEL 呼叫大部分的回呼函式,但您可以強制佇列和檔案物件回呼函式在 IRQL = PASSIVE_LEVEL執行。 (工作專案回呼函式一律在PASSIVE_LEVEL執行。)
框架會使用一組內部同步鎖用來實作此自動同步。 架構可確保兩個以上的線程無法同時呼叫相同的回呼函式。 每個執行緒必須等到可以取得同步鎖,然後再呼叫回呼函式。 (驅動程式也可以在必要時選擇性地取得這些同步鎖定。如需詳細資訊,請參閱 使用 Framework Locks。)
您的驅動程式應該將物件特定資料儲存在 對象內容空間中。 如果您的驅動程式只使用架構定義的介面,則只有接收物件句柄的回呼函式可以存取此數據。 如果架構正在同步處理驅動程式回呼函式的呼叫,一次只會呼叫一個回呼函式。 物件的上下文空間一次只能由一個回呼函式存取。
除非您的驅動程式實作 被動層級中斷處理,否則服務中斷和存取中斷數據的程式代碼必須在裝置的 IRQL (DIRQL) 上執行,而且需要額外的同步處理。 如需詳細資訊,請參閱 同步中斷代碼。
如果您的驅動程式能夠自動同步處理 I/O 要求的回呼函式,架構會同步處理這些回呼函式,以便一次執行一個回呼函式。 下表列出架構同步處理的回呼函式。
物件類型 | 同步處理回呼函式 |
---|---|
Queue 物件 |
|
檔案物件 |
所有 回呼函式 |
請求物件 |
或者,架構也可以將這些回呼函式與驅動程式提供給裝置的任何中斷、DPC、工作專案和定時器物件回呼函式同步處理(不包括中斷物件的 EvtInterruptIsr 回呼函式)。 若要啟用這個額外的同步處理,驅動程式必須將這些物件的組態結構的 AutomaticSerialization 成員設定為 TRUE。
總而言之,架構的自動同步處理功能提供下列功能:
架構一律會同步處理每個裝置的 PnP 和電源管理回呼函式。
架構可以選擇性地同步 I/O 佇列的請求處理程式,以及一些額外的回呼函數(請參閱上表)。
驅動程式可以要求架構將中斷、DPC、工作專案和定時器物件的回呼函式同步執行。
驅動程式必須使用同步中斷程式碼中所述的技術,以同步服務中斷事件和訪問中斷數據的程式碼。
架構不會同步驅動程式的其他回呼函式,例如驅動程式的 CompletionRoutine 回呼函式,或 I/O 目標物件所定義的回呼函式。 相反地,架構會提供驅動程式可用來同步處理這些回呼函式的額外 鎖定 。
選擇同步處理範圍
您可以選擇讓架構同步處理所有與裝置 I/O 佇列相關聯的回呼函式。 或者,您可以選擇讓架構個別同步處理每個裝置 I/O 佇列的回呼函式。 驅動程式可用的同步處理選項如下所示:
裝置層級同步處理
架構會協調裝置的所有 I/O 佇列的回呼函式,使它們依次執行。 架構會先取得裝置的同步鎖,再呼叫回呼函式來達成同步。
佇列級別同步
架構會為每個個別的 I/O 佇列同步回呼函式,確保它們一次只執行一個。 框架藉由在呼叫回呼函式之前取得佇列的同步鎖定來實現同步。
無同步處理
框架不會在執行上表中的回呼函式時同步,也不會在呼叫回呼函式之前獲取同步鎖。 如果需要同步處理,驅動程式必須提供它。
若要指定架構是否要為驅動程式提供裝置層級同步處理、佇列層級同步處理,或未為驅動程式指定同步處理範圍,請為驅動程式物件、裝置物件或佇列物件指定 同步處理範圍 。 物件的WDF_OBJECT_ATTRIBUTES結構的 SynchronizationScope 成員會識別物件的同步處理範圍。 驅動程式可以指定的同步處理範圍值如下:
WdfSynchronizationScopeDevice
架構會藉由取得裝置物件的同步鎖來同步。
WdfSynchronizationScopeQueue
架構透過獲取佇列物件的同步鎖來進行同步。
WdfSynchronizationScopeNone
架構不會同步,也不會取得同步鎖。
WdfSynchronizationScopeInheritFromParent
架構會從物件的父物件取得物件的 SynchronizationScope 值。
一般而言,我們不建議使用裝置層級同步處理。
如需同步處理範圍值的詳細資訊,請參閱 WDF_SYNCHRONIZATION_SCOPE。
驅動程式對象的預設同步處理範圍是 WdfSynchronizationScopeNone。 裝置和佇列對象的預設同步處理範圍是 WdfSynchronizationScopeInheritFromParent。
若要使用架構為所有裝置提供裝置層級同步處理,請將SynchronizationScope設定為驅動程式驅動程式物件的WDF_OBJECT_ATTRIBUTES結構中的 WdfSynchronizationScopeDevice。 針對每個裝置物件使用預設 WdfSynchronizationScopeInheritFromParent 值。
若要為個別裝置提供裝置層級同步處理,請使用驅動程式對象的預設 WdfSynchronizationScopeNone 值。 在個別裝置物件的WDF_OBJECT_ATTRIBUTES結構中,將 SynchronizationScope 設定為 WdfSynchronizationScopeDevice。
如果您要架構提供裝置的佇列層級同步處理,您可以使用下列技術:
針對 Framework 1.9 版和更新版本,請在佇列物件的 WDF_OBJECT_ATTRIBUTES 結構中設定 WdfSynchronizationScopeQueue,以啟用個別佇列的佇列層級同步處理。 這是慣用的技術。
或者,您可以在所有架構版本中使用下列步驟:
- 在裝置物件的WDF_OBJECT_ATTRIBUTES結構中,將 SynchronizationScope 設定為 WdfSynchronizationScopeQueue。
- 針對每個裝置的佇列物件,使用預設 WdfSynchronizationScopeInheritFromParent 值。
如果您不想讓架構同步處理處理驅動程式 I/O 要求的回呼函式,請使用驅動程式驅動程式、裝置和佇列物件的預設 SynchronizationScope 值。 在此情況下,架構不會自動同步驅動程式的 I/O 要求相關回呼函式。 架構可以在 IRQL <= DISPATCH_LEVEL 呼叫回呼函式。
設定 SynchronizationScope 值只會同步處理上一個數據表所包含的回呼函式。 如果您想要架構也同步處理驅動程式的中斷、DPC、工作專案和定時器物件回呼函式,請將這些物件的組態結構的 AutomaticSerialization 成員設定為 TRUE。
不過,只有當您想要在相同的 IRQL 上同步執行的所有回呼函式時,才能將 AutomaticSerialization 設定為 TRUE 。 選擇下一個描述的執行 層級可能會導致不相容的 IRQL 層級。 在這種情況下,驅動程式必須使用 架構鎖定 ,而不是設定 AutomaticSerialization。 如需中斷、DPC、工作專案和定時器物件之組態結構的詳細資訊,以及有關在這些結構中設定 AutomaticSerialization 之限制的詳細資訊,請參閱 WDF_INTERRUPT_CONFIG、 WDF_DPC_CONFIG、 WDF_WORKITEM_CONFIG和 WDF_TIMER_CONFIG。
如果您將 AutomaticSerialization 設定為 TRUE,請選取佇列層級同步處理。
選擇執行層級
當驅動程式建立某些類型的架構物件時,它可以指定對象的 執行層級 。 執行層級會指定 IRQL,架構會呼叫處理驅動程式 I/O 要求的 物件事件回呼函式。
如果驅動程式提供執行層級,提供的層級會影響佇列和檔案物件的回呼函式。 一般而言,如果驅動程式使用自動同步處理,架構會在 IRQL = DISPATCH_LEVEL呼叫這些回呼函式。 藉由指定執行層級,驅動程式可以強制架構在 IRQL = PASSIVE_LEVEL呼叫這些回呼函式。 當設定呼叫佇列和檔案物件回呼函式的 IRQL 時,架構會使用下列規則:
如果驅動程式使用自動同步處理,除非驅動程式要求架構在 IRQL = PASSIVE_LEVEL 呼叫其回呼函式,否則會在 IRQL = DISPATCH_LEVEL呼叫其佇列和檔案物件回呼函式。
如果驅動程式未使用自動同步處理,且未指定執行層級,則可以在 IRQL <= DISPATCH_LEVEL呼叫驅動程式的佇列和檔案物件回呼函式。
如果您的驅動程式提供檔案物件回呼函式,您最可能希望架構在 IRQL = PASSIVE_LEVEL呼叫這些回呼函式,因為某些檔案數據,例如檔名,是可分頁的。
若要提供執行層級,您的驅動程式必須指定物件WDF_OBJECT_ATTRIBUTES結構之 ExecutionLevel 成員的值。 驅動程式可以指定的執行層級值如下:
WdfExecutionLevelPassive
架構會在 IRQL = PASSIVE_LEVEL 呼叫物件的回呼函式。
WdfExecutionLevelDispatch
架構可以在 IRQL <= DISPATCH_LEVEL 呼叫物件的回呼函式。 如果驅動程式使用自動同步處理,架構一律會在 IRQL = DISPATCH_LEVEL呼叫回呼函式。
WdfExecutionLevelInheritFromParent
架構會從物件的父代取得物件的 ExecutionLevel 值。
驅動程式對象的預設執行層級是 WdfExecutionLevelDispatch。 所有其他對象的預設執行層級為 WdfExecutionLevelInheritFromParent。
如需執行層級值的詳細資訊,請參閱 WDF_EXECUTION_LEVEL。
下表顯示 IRQL 層級,架構可以針對佇列物件和檔案物件呼叫驅動程式的回呼函式。
同步處理範圍 | 執行層級 | 佇列和檔案回呼函式的 IRQL |
---|---|---|
WdfSynchronizationScopeDevice |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeDevice |
WdfExecutionLevelDispatch |
調度層級 |
WdfSynchronizationScopeQueue |
WdfExecutionLevelPassive |
被動層級 |
WdfSynchronizationScopeQueue |
WdfExecutionLevelDispatch |
調度級別 (DISPATCH_LEVEL) |
WdfSynchronizationScopeNone |
WdfExecutionLevelPassive |
被動層級 |
WdfSynchronizationScopeNone |
WdfExecutionLevelDispatch |
<= DISPATCH_LEVEL |
您可以將執行層級設定為 WdfExecutionLevelPassive 或 WdfExecutionLevelDispatch 的驅動程式、裝置、檔案、佇列、定時器和一般物件。 對於其他物件,只允許 WdfExecutionLevelInheritFromParent 。
您應該在以下情況下指定 WdfExecutionLevelPassive:
驅動程式的回呼函式必須呼叫框架方法或只能在 IRQL = PASSIVE_LEVEL 呼叫的 Windows 驅動程式模型 (WDM) 例程。
驅動程式的回呼函式必須存取可分頁的程式代碼或數據。 例如,檔案物件回呼函式通常會存取可分頁的數據。
驅動程式可以設定WdfExecutionLevelDispatch,而不是設定WdfExecutionLevelPassive,並提供回呼函式,以便在必須在 IRQL = PASSIVE_LEVEL 處理某些作業時建立工作專案。
在您決定驅動程式是否應該將對象的執行層級設定為 WdfExecutionLevelPassive 之前,請先判斷呼叫驅動程式堆疊中的驅動程式和其他驅動程式的 IRQL。 請考慮下列情況:
如果您的驅動程式位於內核模式驅動程式堆疊的頂端,系統通常會在 IRQL = PASSIVE_LEVEL 呼叫驅動程式。 這類驅動程式的用戶端可能是UMDF型驅動程式或使用者模式應用程式。 指定 WdfExecutionLevelPassive 並不會對驅動程式的效能造成負面影響,因為框架不需要將驅動程式的呼叫排入佇列,以便在 IRQL = PASSIVE_LEVEL 的工作項目中呼叫。
如果您的驅動程式不在堆疊頂端,系統很可能不會在 IRQL = PASSIVE_LEVEL 呼叫您的驅動程式。 因此,架構必須將驅動程式對工作專案的呼叫排入佇列,這些專案稍後會在 IRQL = PASSIVE_LEVEL呼叫。 相較於允許在 IRQL <= DISPATCH_LEVEL 呼叫驅動程式的回呼函式,此過程可能會導致驅動程式效能不佳。
對於 DPC 物件,以及不代表被動層級定時器的定時器物件,如果您將父裝置的執行層級設定為 WdfExecutionLevelPassive,則無法將組態結構的 AutomaticSerialization 成員設定為 TRUE。 架構會在 IRQL = PASSIVE_LEVEL 時取得裝置物件的 回呼同步處理鎖定。 因此,它無法使用鎖定來同步處理必須在 IRQL = DISPATCH_LEVEL執行的 DPC 或定時器物件回呼函式。 在此情況下,您的驅動程式應該在任何需要彼此同步的裝置、DPC 或定時器物件的回呼函式中使用 框架自旋鎖。
另請注意,對於確實代表被動層級定時器的定時器物件,只有當父裝置的執行層級設定為 WdfExecutionLevelPassive時,才能將組態結構的 AutomaticSerialization 成員設定為 TRUE。