迷你驅動程式或迷你埠驅動程式作為驅動程式對中的一個部分運作。 驅動程式配對,例如(迷你埠、埠)可讓驅動程序開發變得更容易。 在驅動程式組中,一個驅動程式會處理整個裝置集合通用的一般工作,而另一個驅動程式則處理個別裝置特有的工作。 處理裝置特定工作的驅動程式會依各種名稱執行,包括迷你埠驅動程式、迷你類別驅動程式和迷你驅動程式。
Microsoft提供一般驅動程式,而且通常獨立硬體廠商會提供特定的驅動程式。 閱讀本主題之前,您應該先瞭解 裝置節點和裝置堆疊中所呈現的概念, 和 I/O 要求封包。
每個內核模式驅動程式都必須實作名為 DriverEntry的函式,此函式會在載入驅動程式后不久呼叫。 DriverEntry 函式會填入 DRIVER_OBJECT 結構的某些成員,並具有驅動程式所實作之數個其他函式的指標。 例如,DriverEntry 函式會填入 DRIVER_OBJECT 結構的 Unload 成員,並具有驅動程式 Unload 函式的指標,如下圖所示。
MajorFunctionDRIVER_OBJECT 結構的成員是處理 I/O 要求封包的函式指標陣列(IRPs),如下圖所示。 一般而言,驅動程式會填入數個 MajorFunction 陣列成員,其中包含處理各種 IRP 之函式的指標(由驅動程序實作)。
IRP 可以根據其主要函式程式碼進行分類,其是由常數識別,例如 IRP_MJ_READ、IRP_MJ_WRITE或 IRP_MJ_PNP。 識別主要函式程式代碼的常數,可作為 MajorFunction 陣列中的索引。 例如,假設驅動程式實作分派函式來處理具有主要函式程式碼的 IRP IRP_MJ_WRITE。 在此情況下,驅動程式必須使用指向分派函式的指標填入陣列的 MajorFunction[IRP_MJ_WRITE] 此元素。
驅動程式通常會填入 MajorFunction 陣列的某些元素,並將其餘元素設定為 I/O 管理員所提供的預設值。 下列範例示範如何使用 "" "" "!drvobj" "" "" 調試器擴充功能來檢查並口驅動程式的函式指標。
0: kd> !drvobj parport 2
Driver object (fffffa80048d9e70) is for:
\Driver\Parport
DriverEntry: fffff880065ea070 parport!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff880065e131c parport!PptUnload
AddDevice: fffff880065d2008 parport!P5AddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880065d49d0 parport!PptDispatchCreateOpen
[01] IRP_MJ_CREATE_NAMED_PIPE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE fffff880065d4a78 parport!PptDispatchClose
[03] IRP_MJ_READ fffff880065d4bac parport!PptDispatchRead
[04] IRP_MJ_WRITE fffff880065d4bac parport!PptDispatchRead
[05] IRP_MJ_QUERY_INFORMATION fffff880065d4c40 parport!PptDispatchQueryInformation
[06] IRP_MJ_SET_INFORMATION fffff880065d4ce4 parport!PptDispatchSetInformation
[07] IRP_MJ_QUERY_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL fffff880065d4be8 parport!PptDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880065d4c24 parport!PptDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP fffff880065d4af4 parport!PptDispatchCleanup
[13] IRP_MJ_CREATE_MAILSLOT fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER fffff880065d491c parport!PptDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL fffff880065d4d4c parport!PptDispatchSystemControl
[18] IRP_MJ_DEVICE_CHANGE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP fffff880065d4840 parport!PptDispatchPnp
在調試程序輸出中,您可以看到 parport.sys 實作 GsDriverEntry,這是驅動程式的進入點。 GsDriverEntry,這是在驅動程式建置時自動生成的,會先執行一些初始化,然後呼叫由驅動程式開發人員實作的 DriverEntry。
您也可以看到 parport 驅動程式(在其 DriverEntry 函式中)提供指標來指向分派這些主要功能碼的函式:
- IRP_MJ_CREATE
- IRP_MJ_CLOSE
- IRP_MJ_READ
- IRP_MJ_WRITE
- IRP_MJ_QUERY_INFORMATION
- IRP_MJ_SET_INFORMATION
- IRP_MJ_DEVICE_CONTROL
- IRP_MJ_INTERNAL_DEVICE_CONTROL
- IRP_MJ_CLEANUP
- IRP_MJ_POWER
- IRP_MJ_SYSTEM_CONTROL
- IRP_MJ_PNP
MajorFunction 陣列的其餘元素 存有預設分派函式的指標 nt!IopInvalidDeviceRequest。
在偵錯器輸出中,您可以看到剖析驅動程式為 Unload 和 AddDevice提供函式指標,但沒有提供 StartIo的函式指標。 AddDevice 函式不尋常,因為它的函式指標不會儲存在 DRIVER_OBJECT 結構中。 反之,它會儲存在 DRIVER_OBJECT 結構的擴展中的 AddDevice 成員中。 下圖說明剖析驅動程式在其 DriverEntry 函式中提供的函式指標。 由 parport 提供的函式指標被遮罩。
使用驅動程式配對來簡化
在一段時間內,隨著 微軟 內外的驅動程式開發人員獲得 Windows 驅動程式模型(WDM)的經驗,他們意識到了一些關於調度函數的見解:
- 分派函式基本上是樣板。 例如,IRP_MJ_PNP分派函式中的大部分程式代碼都與所有驅動程式相同。 它只是隨插即用(PnP)程式代碼的一小部分,專門用於控制單一硬體的單一驅動程式。
- 分派函式非常複雜且難以正確執行。 實作線程同步處理、IRP 佇列和 IRP 取消等功能具有挑戰性,而且需要深入瞭解作系統的運作方式。
為了讓驅動程式開發人員更容易,Microsoft建立了數個技術特定的驅動程式模型。 乍一看,技術特定的模型似乎彼此大不相同,但仔細觀察顯示,其中許多模型都是以這種模式為基礎:
- 驅動程式分成兩個部分:一個處理一般處理,另一個處理特定裝置特定的處理。
- 一般作品是由Microsoft所撰寫。
- 特定部分可由Microsoft或獨立硬體廠商所撰寫。
假設 Proseware 和 Contoso 公司都製作了需要 WDM 驅動程式的玩具機器人。 此外,假設Microsoft提供稱為 GeneralRobot.sys的一般機器人驅動程式。 Proseware 和 Contoso 可以撰寫小型驅動程式,以處理其特定機器人的需求。 例如,Proseware 可以撰寫 ProsewareRobot.sys,而且可以結合成一組驅動程式(ProsewareRobot.sys、GeneralRobot.sys),以形成單一 WDM 驅動程式。 同樣地,兩組驅動程式(ContosoRobot.sys、GeneralRobot.sys)可以結合成單一 WDM 驅動程式。 其最一般的形式是,您可以使用 (specific.sys、 general.sys) 配對來建立驅動程式。
驅動程式配對中的 函式指標
在 (specific.sys、general.sys) 組中,Windows 會載入 specific.sys,並呼叫其 DriverEntry 函式。 specific.sys DriverEntry 函式會接收一個指向 DRIVER_OBJECT 結構的指標。 通常,您會預期 DriverEntry 會將數個元素填入 MajorFunction 陣列,其中包含分派函式的指標。 此外,您還需要 DriverEntry 填入 DRIVER_OBJECT 結構的 Unload 成員(以及 StartIo 成員),以及驅動程式物件延伸模組的 AddDevice 成員。 不過,在驅動程式配對模型中,DriverEntry 不一定這樣做。 相反地,specific.sys DriverEntry 函式會將 DRIVER_OBJECT 結構傳遞至 general.sys所實作的初始化函式。 下列程式代碼範例示範如何在 (ProsewareRobot.sys、 GeneralRobot.sys) 配對中呼叫初始化函式。
PVOID g_ProsewareRobottCallbacks[3] = {DeviceControlCallback, PnpCallback, PowerCallback};
// DriverEntry function in ProsewareRobot.sys
NTSTATUS DriverEntry (DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
// Call the initialization function implemented by GeneralRobot.sys.
return GeneralRobotInit(DriverObject, RegistryPath, g_ProsewareRobottCallbacks);
}
GeneralRobot.sys 中的初始化函式會將函式指標寫入 DRIVER_OBJECT 結構的適當成員(及其延伸模組),以及 MajorFunction 陣列的適當元素。 其概念是,當 I/O 管理員將 IRP 傳送給驅動程式組時,IRP 會先移至由 GeneralRobot.sys實作的分派函式。 如果 GeneralRobot.sys 可以自行處理 IRP,則特定驅動程式 ProsewareRobot.sys不需要參與。 如果 GeneralRobot.sys 可以處理部分但並非全部的 IRP 處理,它會從 ProsewareRobot.sys所實作的其中一個回呼函式取得協助。 GeneralRobot.sys 會接收 GeneralRobotInit 呼叫中 ProsewareRobot 回呼的指標。
在 DriverEntry 傳回之後的某個時間點,Proseware Robot 裝置節點會建構出一個裝置堆疊。 裝置堆疊看起來可能像這樣。
如上圖所示,Proseware Robot 的裝置堆疊有三個裝置物件。 最上層裝置物件是與篩選驅動程式相關聯的篩選裝置物件 (Filter DO),AfterThought.sys。 中間裝置物件是與驅動程式組相關聯的功能性裝置物件 (FDO)(ProsewareRobot.sys,GeneralRobot.sys)。 驅動程式組可作為裝置堆疊的函式驅動程式。 底部裝置物件是與 Pci.sys相關聯的實體裝置物件 (PDO)。
請注意,驅動程式組只會佔用裝置堆疊中的一個層級,且只與一個裝置對象相關聯:FDO。 GeneralRobot.sys 處理 IRP 時,它可能會呼叫 ProsewareRobot.sys 以取得協助,但這與將要求傳遞至裝置堆棧不同。 驅動程式組會形成位於裝置堆疊中一個層級的單一 WDM 驅動程式。 驅動程序組會完成 IRP,或將它向下傳遞至設備堆疊中的 PDO,而 PDO 與 Pci.sys相關聯。
驅動程式組的範例
假設您的筆記型電腦中有無線網卡,並且在設備管理器中查看後可判斷 netwlv64.sys 是網路卡的驅動程式。 您可以使用 !drvobj 除錯器擴充套件來檢查與 netwlv64.sys相關的函式指標。
1: kd> !drvobj netwlv64 2
Driver object (fffffa8002e5f420) is for:
\Driver\netwlv64
DriverEntry: fffff8800482f064 netwlv64!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff8800195c5f4 ndis!ndisMUnloadEx
AddDevice: fffff88001940d30 ndis!ndisPnPAddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880018b5530 ndis!ndisCreateIrpHandler
[01] IRP_MJ_CREATE_NAMED_PIPE fffff88001936f00 ndis!ndisDummyIrpHandler
[02] IRP_MJ_CLOSE fffff880018b5870 ndis!ndisCloseIrpHandler
[03] IRP_MJ_READ fffff88001936f00 ndis!ndisDummyIrpHandler
[04] IRP_MJ_WRITE fffff88001936f00 ndis!ndisDummyIrpHandler
[05] IRP_MJ_QUERY_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[06] IRP_MJ_SET_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[07] IRP_MJ_QUERY_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[08] IRP_MJ_SET_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[09] IRP_MJ_FLUSH_BUFFERS fffff88001936f00 ndis!ndisDummyIrpHandler
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0c] IRP_MJ_DIRECTORY_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0e] IRP_MJ_DEVICE_CONTROL fffff8800193696c ndis!ndisDeviceControlIrpHandler
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880018f9114 ndis!ndisDeviceInternalIrpDispatch
[10] IRP_MJ_SHUTDOWN fffff88001936f00 ndis!ndisDummyIrpHandler
[11] IRP_MJ_LOCK_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[12] IRP_MJ_CLEANUP fffff88001936f00 ndis!ndisDummyIrpHandler
[13] IRP_MJ_CREATE_MAILSLOT fffff88001936f00 ndis!ndisDummyIrpHandler
[14] IRP_MJ_QUERY_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[15] IRP_MJ_SET_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[16] IRP_MJ_POWER fffff880018c35e8 ndis!ndisPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL fffff880019392c8 ndis!ndisWMIDispatch
[18] IRP_MJ_DEVICE_CHANGE fffff88001936f00 ndis!ndisDummyIrpHandler
[19] IRP_MJ_QUERY_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1a] IRP_MJ_SET_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1b] IRP_MJ_PNP fffff8800193e518 ndis!ndisPnPDispatch
在調試程序輸出中,您可以看到 netwlv64.sys 實作 GsDriverEntry,這是驅動程式的進入點。 建置驅動程式時自動產生的 GsDriverEntry,會執行一些初始化,然後呼叫驅動程式開發人員所撰寫 DriverEntry。
在此範例中,netwlv64.sys 會實作 DriverEntry,但 ndis.sys 實作 AddDevice、Unload,並且實作數個分派函式。 Netwlv64.sys 稱為 NDIS 迷你埠驅動程式,ndis.sys 稱為 NDIS 連結庫。 這兩個模組一起形成一對 (NDIS miniport, NDIS 函式庫)配對。
此圖顯示無線網路卡的裝置堆疊。 請注意,驅動程式組 (netwlv64.sys、 ndis.sys) 只佔用裝置堆疊中的一個層級,且只與一個裝置對象相關聯:FDO。
可用的驅動程式組
不同的技術特定驅動程式模型會針對驅動程式組的特定和一般部分使用各種名稱。 在許多情況下,配對的特定部分具有字首 “mini”。以下是一些可用的(特定、一般)對:
- (顯示迷你連接埠驅動程序,顯示埠驅動程式)
- (音訊迷你埠驅動程式、音訊埠驅動程式)
- (記憶體迷你埠驅動程序,儲存埠驅動程式)
- (電池迷你分類驅動程式,電池類別驅動程式)
- (HID 迷你驅動程式、HID 類別驅動程式)
- (變更微類驅動程式,變更埠驅動程式)
- (NDIS 迷你埠驅動程式,NDIS 函式庫)
注意 如您在清單中看到,數個模型會針對驅動程式組的一般部分使用 類別驅動程式 一詞。 這種類別驅動程式與獨立類別驅動程式不同,與類別篩選驅動程式不同。
相關主題
所有驅動程式開發人員 概念