解讀Windows 2000/XP分層驅動模型

來源:互聯網
上載者:User
解讀Windows 2000/XP分層驅動模型


  可擴充性是Windows NT/2000/XP設計的目標之一,其分層驅動模型是可擴充性的最好體現。實現分層依賴於IO管理器的兩個重要的設計:1、Windows中的任何一個驅動程式都被設計成Client/Server模式。對於用戶端驅動,通過IoGetDeviceObjectPointer之類的擷取服務端驅動匯出的Device對象,通過IO管理器的IoCallDriver請求服務端的服務。IoCallDriver實際上根據用戶端的調用參數(通過IRP)調用服務端的派遣入口(回呼函數)接受用戶端的請求。2、IO管理器實現一個分層的資料結構,在DEVICE_OBJECT對象中儲存某種關係,自動將請求IRP發給裝置棧中的最高的一個裝置,由其決定如何處理,或是自身處理,或是向下傳遞,達到分層的目的。鑒於這種能力,分層驅動模型可以實現很多應用,如檔案監控,加密,防病毒等等,由於PNP的引入,這種應用將更加廣泛。實際上這種分層模型在Windows NT/2000/XP中無處不在,不信的話,請執行如下命令看看:

  findstr /M IoAttachDevice %SYSTEMROOT%/system32/drivers/*.sys

  所有列出的Driver幾乎都可以看作分層驅動的例子。對於分層驅動的介紹幾乎充斥著所有介紹Windows驅動的任何一本書中。本文不想過多於重複這些內容,旨在從底層資料結構的實現上說明這種分層的實現。我們首先從DEVICE_OBJECT開始說明。下面是這個結構的部分定義:

  typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT {
      .
      .
      .
    struct _DRIVER_OBJECT *DriverObject;
    struct _DEVICE_OBJECT *NextDevice;
    struct _DEVICE_OBJECT *AttachedDevice;
      .
      .
      .
    PVOID DeviceExtension;
      .
      .
      .
    CCHAR StackSize;
      .
      .
      .
    struct _DEVOBJ_EXTENSION *DeviceObjectExtension;
      .
      .
      .
  } DEVICE_OBJECT;

  成員DriverObject是DeviceObject對應的DRIVER_OBJECT,通過這個對象,IO管理器可以知道如何為這個裝置提供服務(通過調用DriverObject提供的Dispatch入口)。通常一個DRIVER_OBJECT可以為一至多個DEVICE_OBJECT提供服務,各個DEVICE服務不同的同一類型的物理或邏輯裝置。她們也有可能扮演不同的角色,如對於PNP引入的PDO與FDO,下面我會詳細介紹。正因為這樣NextDevice成員即用於連結這些DEVICE_OBJECT。所以可以得到這樣的結論,像前一句所描述的,對於同一個Driver匯出的PDO與FDO則通過NextDevice成員邏輯上建立關係。而對於AttachedDevice成員對於Legacy的Driver(Windows NT 4.0之前,在之後的版本中也可以正常使用),主要通過這一欄位來實現本文開頭提到的IO管理器提供的第二項功能。如下所示,AttacheDevice1附加至Device1之上, 這種情況下AttachDevice1與Device1通常不是由同一個Driver服務的,即他們沒有通過NextDevice成員連結在一起的,這樣我們可以通過書寫另一個Driver,通過附加一個Device至一個已存在的裝置上改變或監視這個裝置的行為。當然這時候Device1的AttachedDevice成員即指向AttachDevice1。而AttachedDevice1並沒有被任何裝置附接過,所以其AttachedDevice成員指向NULL。通過調用IoCallDriver請求Device1服務時,IO管理器內部會調用IoGetAttachedDevice之類的,獲得附接在Device1之上的最高層裝置。這裡是AttachedDevice1,而對於Device2,即是AttachedDevice3,如果沒有附接任何裝置,當然就是Device1了。這樣我們附接的裝置就有機會執行相應的操作了。另外對於Device1與Device2的情況,很明顯,Device1位於Device2之上,而這時就是我們開頭介紹的第一種情況,Device1通過IoCallDriver請求Device2,邏輯上建立一種關係(我們不能通過任何資料結構標識這種關係,通常這是驅動開發人員設計成的邏輯關係,對於Windows 2000/XP上如何知道這種關係,當然最好的出入就是DDK文檔了)。

  |-------------|____AttachDevice1
  |   Device1   |
  |-------------|

                          ------AttachDevice3
  |-------------|____AttachDevice2---|
  |   Device2   |
  |-------------|

  DeviceExtension成員通常是驅動開發人員自已定義的結構,其存放裝置相關的內容,例如用於區別PDO與FDO等等。在調用IoCreateDevice時指定其大小,IO管理器分配sizeof(DEVICE_OBJECT)+DeviceExtensionSize的非分頁式記憶體用於裝置對象,這樣這兩個結構在物理上是連續的,所以我們在一些檔案系統驅動程式中經常看過VOLUME_DEVICE_OBJECT這樣的定義(緊隨標準的DEVICE_OBJECT後即是專用的用於VOLUME的定義,避免在Dispatch入口每次都要引用這個成員)。

  StackSize指當前裝置棧的裝置個數(AttachedDevice個數加上裝置本身),用於IO管理器分配IRP時指定STACK_LOCATION個數。

  上面的所有敘述即構成了Windows NT 4.0之前的Windows分層驅動模型。這也是遺留的(Legacy)的層次驅動程式的主要工作思路。AttachedDevice指出了Attached了的裝置,Microsoft在Windows 2000中的DeviceObjectExtension結構成員中引入了一個AttachedTo指出被當前裝置Attached的低層裝置,這在Windows NT是沒有實現的。DeviceObjectExtension是一個很重要的結構,下面要介紹的支援pnp的WDM驅動的AddDevice入口也在這兒。她是由系統定義的結構(區別於DeviceExtension)。對於DRIVER_OBJECT也有個類似的稱為DriverExtenion的結構,後者有一個ClientDriverExtension結構,由IoAllocateDriverObjectExtension分配,IoGetDriverObjectExtension獲得。classpnp.sys即是通過這一方法,實現對disk.sys、tape.sys與cdrom.sys的管理;NDIS.SYS也通過這一方法實現各個Miniport/IM/Protocol Driver的管理。

  Windows 2000及其以後的NT系列引入了WDM,支援PNP、Power管理及WMI,為了讓作業系統本身就對此支援,ntoskrnl.exe中匯出了幾個置有DRVO_BUILTIN_DRIVER(ntddk.h中定義)標誌的Driver,分別為pnpmanager、WMIxWDM及ACPI_HAL,後者是支援ACPI的HAL(這是在我Windows XP Professional台式機上的情況,不知道不支援ACPI的機子啥模樣,我的機子上ntoskrnl.exe還匯出/FileSystem/Raw驅動用於檔案系統的支援,我想這幾個裝置名稱可能會隨機器配置及Windows版本不同而不同,實際上我在我Windows 2000 Server SP0筆記本上ntoskrnl.exe產生如下四個裝置:Pnpmanager,PCI_HAL,WDM與RAW,我底下的敘述都是基本我台式機上的,至於出現的一些不同我可能會另外指出,也有可能沒有),前兩個driver都是為了支援WDM而引入的。WMIxWDM匯出WMIDataDevice用作WMI的支援,這與本文討論分層驅動沒有關係,以下我重點介紹Pnpmanager。

  在介紹之前,我們來看一下devmgmt.msc“依串連排序裝置”視圖:

  TSU00(機器名,由pnpmanager實現的虛擬Root匯流排枚舉)
      |
      |+ Advanced Configuration and Power Interface(ACPI) PC(ntoskrnl中的ACPI_HAL驅動實現)
        |
        |+Micrsoft ACPI-Compliant System(由acpi.sys枚舉)
            |
            |+PCI bus(由pci.sys實現)
                |
                |+(串連的裝置)

  這是我機子上的情況,pnpmanager是一個匯流排驅動程式(在ntoskrnl.exe內部實現,如果你有checked build的ntoskrnl.exe,你可以很容易的發現其由base/ntos/io/pnpmgr/pnpdd.c實現,看過Sysinternals匯出的Windows XP Source Tree了嗎?),她實現一個稱為Root的虛擬匯流排。所有Legacy裝置,都是串連至這個虛擬匯流排上的,不信的話,你在devmgmt.msc的上面列出的草圖上繼續選“顯示隱藏的裝置看看”。從這種意義上看她要為每一個串連到她上面的裝置建立一個PDO。對於這些PDO通常是以00000001開始的十六進位命名,如在我機子我實驗某一時刻裝置名稱一直至00000050,共80個PDO(在我的Windows 2000 Server的機子上並不是這樣命名的,雖然也是基於十六進位的,但卻是從挺大的一個數值開始的)。我非常喜歡隨Windows XP DDK一些發行的OSR的DeviceTree,但為了更好的理解,還是以windbg作個實驗吧:

  找出上面草圖ACPI_HAL附接的由pnpmanager實現的PDO,通常這是pnpmanager實現的第一個PDO,即命名為00000001(如果你的Pnpmanager產生的裝置不是這樣命名的,請使用!drvobj pnpmanager找出產生的對應的PDO,我的Windows 2000 Server SP0的筆記本上,pnpmanager的第一個PDO用於服務ESS音效卡,而並不是我原以為的PCI_HAL,你可能另需要使用!devstack或是下面要介紹的!devnoe命令,方法不詳述):

  kd> !object /Device/00000001
  Object: 812b4410 Type: (812b4048) Device
    ObjectHeader: 812b43f8
    HandleCount: 0 PointerCount: 5
    Directory Object: e10011b0 Name: 00000001
  kd> !devobj 812b4410
  Device object (812b4410) is for:
  00000001 /Driver/PnpManager DriverObject 812b4980
  Current Irp 00000000 RefCount 0 Type 00000004 Flags 00001040
  Dacl e1518a6c DevExt 812b44c8 DevObjExt 812b44d0 DevNode 812b42b8
  ExtensionFlags (0000000000)
  AttachedDevice (Upper) 812f6bb0 /Driver/ACPI_HAL
  Device queue is not busy.

  我們很容易通過上面的輸出含用AttachedDevice的一行發現串連至裝置00000001的是ACPI_HAL,與我們devmgmt.msc上看到的一致。實際上Attached至這一PDO的是由ACPI_HAL服務的一個稱為FDO的裝置。PDO與FDO本身在內部都仍是由DEVICE_OBJECT來表示的,正像前面提及的對於匯流排驅動開發人員通常使用DeviceExtension中的一個標誌區分同個driver服務的裝置是PDO還是FDO。FDO通常由driver的AddDevice入口建立,建立後使用IoAttachDeviceToDeviceStack附接至下層匯流排驅動提供的PDO,正像上面windbg中我們看到的一樣。這個Attached操作與我們開頭討論的Legacy裝置是一樣的,同樣是使用DEVICE_OBJECT的AttachedDevice成員。

  你可能會困惹pnpmanager的AddDevice入口應該是怎樣實現的。我們知道pnpmanager實現稱為Root的匯流排驅動程式,既然是Root,肯定也就不存在Attached上誰提供的PDO。實際上你可以使用windbg看看其實現:在Free Build XP中,其只實現return STATUS_SUCCESS(xor eax,eax/ret)了。我在用checked build的時候,其只是對IRQL進行檢查:RtlAssert(KeGetCurrentIrql()<=APC_LEVEL)。

  當然對於ACPI_HAL或是PCI匯流排,其AddDevice與pnpmanager實現肯定不同,她們肯定要IoCreateDevice進立FDO,Attached至pnpmanager提供的PDO或是上層匯流排提供的PDO,實現層次關係。我們繼續使用windbg驗證我這種思路:

  kd> !drvobj /Driver/ACPI_HAL
  Driver object (812f6ce8) is for:
  /Driver/ACPI_HAL
  Driver Extension List: (id , addr)

  Device Object list:
  812f6a90 812f6bb0

  ACPI_HAL匯出的兩個裝置哪個是PDO,哪個是FDO呢(你應該會明白至少有一個FDO吧)。我們知道pnp管理器在發現匯流排後,首先會調用匯流排驅動程式的AddDevice入口,然後才會發各種各樣的IRP_MJ_PNP的各種MinorFunction指示匯流排驅動程式枚舉匯流排,對串連至上面的裝置各建立PDO等等。這兒我只是描述通常情況,對於如DDK中附帶的Toaster這樣的虛擬匯流排,其枚舉匯流排上的裝置是通過應用程式發相應的IOCTL來指示PDO的建立(通過IoInvalidateDeviceRelations讓pnp管理器發IRP_MJ_PNP)。當然就算是Toaster實現的這樣虛擬匯流排,及AddDevice入口,即FDO總是先於PDO的建立。因為對於同一個DRIVER_OBJECT服務的裝置,其是通過DRIVER_OBJECT的DeviceObject形成單向鏈表,這個DeviceObject指示鏈表頭,由DEVICE_OBJECT的NextDevice聯結成鏈表。而對於IoCreateDevice建立的裝置,後建立的裝置,總是插入表頭,而FDO基本上總是最先建立的,所以總是在表尾。有了這些分析,對於ACPI_HAL上面的輸出812f6bb0即是FDO,而812f6a90則是PDO。OK,這樣這個PDO,則又是上層acpi.sys實現的匯流排驅動程式的下層PDO了。

  kd> !devobj 812f6a90
  Device object (812f6a90) is for:
  00000052 /Driver/ACPI_HAL DriverObject 812f6ce8
  Current Irp 00000000 RefCount 0 Type 0000002a Flags 00001040
  Dacl e1518a6c DevExt 812f6b48 DevObjExt 812f6b60 DevNode 812f63a8
  ExtensionFlags (0000000000)
  AttachedDevice (Upper) 812ad960 /Driver/ACPI
  Device queue is not busy.

  你看看上層是不是/Driver/ACPI(AttachedDevice行)。而PCI匯流排又是Attached至acpi.sys實現的Micrsoft ACPI-Compliant System上的。注意acpi可不像acpi_hal一樣,只有一個PDO,而PCI匯流排也不是第一個PDO。有了這些知識,我想你也應該可以比較容易的發現pci匯流排附接至哪個pdo吧。一個更簡單的辦法是使用!devstack命令dump PCI匯流排的FDO了。

  kd> !drvobj pci
  Driver object (812ef850) is for:
  /Driver/PCI
  Driver Extension List: (id , addr)

  Device Object list:
  812f39e8 812f3d58 812f4e40 812f4038
  812f0710 812f0908 812f0c58 812f14e8
  812f0e38
  kd> !devstack 812f0e38
    !DevObj   !DrvObj         !DevExt   ObjectName
  > 812f0e38 /Driver/PCI     812f0ef0
    812dc8c0 /Driver/ACPI     812f5660 00000058
  !DevNode 812f1008 :
    DeviceInst is "ACPI/PNP0A03/2&daba3ff&0"
    ServiceName is "pci"

  devstack命令實際上使用DEVICE_OBJECT的AttachedDevice與存於DeviceObjectExtension(注意這兒是DeviceObjectExtension而不是DeviceExtension)結構成員中的AttachedTo來顯示裝置棧的。當然devstack命令還顯示裝置對應的DEVICE_NODE。DEVICE_NODE是為了支援pnp而引入的一個系統資料結構,完整的DEVICE_NODE定義是非常複雜且非常龐大的,我就不列出來了,幾個重要的成員如Sibling(兄弟DEVICE_NODE),Child(子DEVICE_NODE),Parent(父DEVICE_NODE),裝置狀態PNP_DEVNODE_STATE,資源使用方式CM_RESOURCE_LIST,介面類型INTERFACE_TYPE,裝置標識、服務名ServiceName等等。

  從我的提示DeviceNode含有Sibling、Child、Parent等成員,我們很容易想到系統可能會將所有DeviceNode組成一個樹(與檔案系統的分類樹類似),實際上正是這樣的,核心變數IopRootDeviceNode指示這顆樹的根。!devnode命令可以看出這個根節點的情況,如果你使用!devnode 0 1命令的話,你將活生生的看到一個windbg的devmgmt.msc版。實際上系統的SetupDi(setupapi.dll匯出的用於裝置安裝的API)就是通過這個來dump出所有的裝置的。devmgmt.msc間接的使用這些API(你可別像我一樣訝異.msc檔案只是一個由MMC.EXE解析的XML檔案)。同樣OSR的DeviceTree肯定也使用了這些API。

  到現在為止你可能更加困惹,啥是啥的PDO,系統如何知道哪個匯流排附接至哪兒,以形成裝置層次。秘密在於註冊表,裝置安裝時通過.inf檔案等向註冊表加入內容指示系統的載入順序。早先的.inf檔案真的是好複雜,在我看來絕不亞於perl指令碼。Windows 2000為你自動做了太多太多了(我真想知道到底做了什麼,嘻嘻)。

  註冊表中HKLM/SYSTEM/CurrentControlSet/Enum與HKLM/SYSTEM/CurrentControlSet/Control/Class共同協作來完成這樣的任務,當然與Legacy驅動程式一樣離不開HKLM/SYSTEM/CurrentControlSet/Services了。為了完整的傳述Windows 2000/XP分層驅動模型,有必要在最後提及一下Filter驅動程式,這是附加在匯流排驅動程式或是其他驅動上或下的一類驅動,用於增強,改變原有裝置的某些功能。由剛提及的註冊表中的Enum與Class項的UpperFilters與LowerFilters的值提供。

  最後,說明一下,由Windows 2000/XP的這個分層驅動區分出很多概念,如中介層驅動程式,故名思義,如Windows 2000/XP中隨處可見的類驅動程式。類驅動程式實現某一類裝置的共同功能,沒有牽涉到實際硬體的訪問。如磁碟類驅動程式,在Windows 2000/XP中有disk.sys,tape.sys,cdrom.sys,他們均藉助於classpnp.sys實作類別驅動程式,以disk.sys為例,她根本不管是IDE介面或是SCSI介面,由底層的atapi.sys或是scsiport.sys這些miniport/port驅動實現與特定硬體的互動。

  另外再提及一點,可以說FileSystem Filter是一個Legacy的分層驅動(只使用DEVICE_OBJECT的AttachedDevice成員。而對於網路驅動程式,如NDIS Intermediate Drivers(含NDIS Filter Intermediate Drivers與NDIS MUX Intermediate Drivers)也可以看作是分層驅動的應用,只不過在Windows 2000/XP中由Ndis Wrapper Library(ndis.sys)隱藏了太多的資訊(隱藏了使用IRP的真正面目),也可以這麼說ndis.sys使用其內部自身的結構定義,如NDIS_M_DRIVER_BLOCK、NDIS_MINIPORT_BLOCK、NDIS_PROTOCOL_BLOCK、NDIS_OPEN_BLOCK這些定義之間的微妙關係,自身實現了一個層次化的結構。

  基本上講了個大概,不知道說得清楚不清楚。期待你的反饋指導交流(tsu00@263.net)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.