天天看點

45、Windows驅動程式模型筆記(三)

4、DriverUnload例程

    在WDM驅動程式中,DriverUnload例程的作用就是釋放DriverEntry例程在全局初始化過程中申請的任何資源,但它幾乎沒什麼可做。如果你在DriverEntry中備份了RegistryPath串,應該在這裡釋放備份所占用的記憶體。

VOID DriverUnload(PDRIVER_OBJECT DriverObject)

{

RtlFreeUnicodeString(&servkey);

}

    如果DriverEntry例程傳回一個失敗狀态代碼,系統将不再調用DriverUnload例程。是以,不能讓DriverEntry例程出錯後産生任何副作用,必須在它傳回錯誤代碼前消除副作用。

5、驅動程式再初始化例程

    I/O管理器提供了一個服務函數:IoRegisterDriverReinitialization。它可以為非WDM驅動程式解決一個奇特的問題。這裡我想解釋一下這個問題,從中你可以知道為什麼WDM驅動程式不用擔心問題。非WDM驅動程式需要在DriverEntry例程中枚舉它的硬體,并在其硬體的所有可能執行個體被識别前裝入記憶體并初始化。例如,滑鼠和鍵盤裝置就是這樣。假定DriverEntry枚舉了所有滑鼠或鍵盤硬體并為它們建立了裝置對象,但如果DriverEntry例程運作的太快,那麼這些驅動程式将不能正常工作。是以,它們必須使用 IoRegisterDriverReinitialization函數寄存一個例程,之後I/O管理器在某個驅動程式檢測到新硬體存在時再回調這個寄存例程。最後“再初始化例程”運作,同時也把自身寄存為下一次回調的函數。

    WDM驅動程式不需要寄存再初始化例程,因為它們不需要用自己的代碼去檢測硬體。PnP管理器自動把新硬體比對到正确的WDM驅動程式上,并調用該驅動程式的AddDevice例程,再由AddDevice例程做所有必要的初始化工作。

6、AddDevice例程

    一個驅動程式可以被多個裝置利用。WDM驅動程式有一個特殊的AddDevice函數,PnP管理器為每個裝置執行個體調用該函數

NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)

    DriverObject指向一個驅動程式對象(在DriverEntry例程中初始化的那個驅動程式對象)。pdo參數指向裝置堆棧底部的實體裝置對象。

對于功能驅動程式,其AddDevice函數的基本職責是建立一個裝置對象并把它連接配接到以pdo為底的裝置堆棧中。相關步驟如下:

1.調用IoCreateDevice建立裝置對象,并建立一個私有的裝置擴充對象。

2.寄存一個或多個裝置接口,以便應用程式能知道裝置的存在。另外,還可以給出裝置名并建立符号連接配接。

3.初始化裝置擴充和裝置對象的Flag成員。

4.調用IoAttachDeviceToDeviceStack函數把新裝置對象放到堆棧上。

IoCreateDevice的第6個參數:

    排斥屬性僅關系到打開請求的目标是命名裝置對象。如果你遵守Microsoft推薦的WDM驅動程式設計方針,沒有為裝置對象命名,那麼打開請求将直接指向PDO。PDO通常不能被标記為排斥,因為總線驅動程式沒有辦法知道裝置是否需要排斥特征。把PDO标為排斥的唯一的機會在系統資料庫中,即裝置硬體鍵或類鍵的Properties子鍵含有Exclusive超越值。為了完全避免依賴排斥屬性,你應該利用IRP_MJ_CREAT例程彈出任何有違規行為的打開請求。

7、為裝置命名

    David Solomon在《Inside Windows NT, Second Edition (Microsoft Press, 1998)》的第三章“System Mechanisms”中給出了關于Windows NT對象管理器和命名空間的一個比較完整的闡述。

    通常裝置對象都把自己的名字放到\Device目錄中。裝置的名稱有兩個用途;第一個用途,裝置命名後,其它核心模式部件可以通過調用IoGetDeviceObjectPointer函數找到該裝置,找到裝置對象後,就可以向該裝置的驅動程式發送IRP。另一個用途,允許應用程式打開命名裝置的句柄,這樣它們就可以向驅動程式發送IRP。應用程式可以使用标準的CreateFile API打開命名裝置句柄,然後用ReadFile、WriteFile,和DeviceIoControl向驅動程式送出請求。應用程式打開裝置句柄時使用\\.\路徑字首而不是标準的UNC(統一命名約定)名稱,如C:\MYFILE.CPP或\\FRED\C-Drive\HISFILE.CPP。在内部,I/O管理器在執行名稱搜尋前自動把\\.\轉換成\??\。為了把\??目錄中的名字與名字在其它目錄(例如,在\Device目錄)中的對象相連接配接,對象管理器實作了一種稱為符号連接配接(symbolic link)的對象。

    符号連接配接有點象桌面上的快捷方式,符号連接配接在Windows NT中的主要用途是把處于清單前面的DOS形式的名稱連接配接到裝置上。用CreateFile打開名稱為“C:\MYFILE.CPP”的對象,對象管理器将以下面過程打開該檔案:

1.核心模式代碼最開始看到的名稱是\??\C:\MYFILE.CPP。對象管理器在根目錄中查找“??”。

2.找到\??目錄後,對象管理器在其中查找“C:”。它發現找到的對象是一個符号連接配接,是以它就用這個符号連接配接組成一個新的核心模式路徑名:\Device\HarddiskVolume1\MYFILE.CPP,然後析取它。

3.使用新路徑名後,對象管理器重新在根目錄中查找“Device”。

4.找到\Device目錄後,對象管理器在其中查找“HarddiskVolume1”,最後它找到一個以該名字命名的裝置。

如果命名了裝置對象,那麼任何核心模式程式都可以打開該裝置的句柄。另外,任何核心模式或使用者模式程式都能建立連接配接到該裝置的符号連接配接,并可以使用這個符号連接配接打開裝置的句柄。

是以,由于超越的影響,你不應該命名你的裝置對象。應用程式仍可以使用注冊的接口(interface)通路你的裝置。

在調用IoCreateDevice時指定裝置特征參數為FILE_DEVICE_SECURE_OPEN。該标志将使Windows 在額外名稱部件存在的情況下仍檢查調用者是否有權限打開裝置句柄。

如果決定命名裝置對象,通常應該把對象名放在名稱空間的\Device分支中。

UNICODE_STRING devname;

RtlInitUnicodeString(&devname, L"\\Device\\Simple0");

IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &devname, ...);

\??目錄以前叫做\DosDevices。\DosDevice仍可以使用,但它本身是\??目錄的符号連接配接。由于裝置對象不能複制到控制台事務的外邊,而符号連接配接可以,是以你應該在\Device目錄中儲存裝置命名,而在\DosDevices目錄中放一個符号連接配接。

8、GUID

GUID用于辨別軟體接口,它與COM(部件對象模型)中用于辨別COM接口的辨別符相同,它還用于OSF(開放軟體基金)的DCE(分布式計算環境) 中,辨別RPC(遠端過程調用)目标。參考Kraig Brockschmidt的《Inside OLE, Second Edition (Microsoft Pres 1995)》第66頁,介紹了更多相關的知識,原始算法規範由OSF制定,相關部分見http://www.opengroup.org/onlinepubs/9629399/apdxa.htm

GUIDGEN生成GUID,用有意義的名字換掉<<name>>,如GUID_SIMPLE,并把這個定義包含到驅動程式或應用程式中。

調用IoRegisterDeviceInterface函數,注冊裝置接口。注冊過程實際就是先建立一個符号連接配接名,然後再把它存入系統資料庫。之後,當響應PnP請求IRP_MN_START_DEVICE時,驅動程式将調用IoSetDeviceInterfaceState函數“使能”該接口。為接口名最終指向PDO,是以PDO的安全描述符将最終控制裝置的通路權限。

9、其它全局性的裝置初始化操作

初始化裝置擴充

當調用IoDeleteDevice删除這個裝置對象時,需要使用一個自旋鎖來解決同步安全問題。初始化預設的DPC對象

許多裝置使用中斷來報告操作完成。中斷處理對中斷服務例程(ISR)能做什麼做了嚴格的限定。特别是ISR不能調用用于報告IRP完成的例程 (IoCompleteRequest)。利用DPC(推遲過程調用)可以繞過這個限制。你的裝置對象中應包含一個輔助DPC對象,它可以排程你的DPC 例程,該對象應該在裝置對象建立後不久被初始化。

設定緩沖區對齊掩碼[1]

初始化裝置标志

建立裝置堆

每個過濾器驅動程式和功能驅動程式都有責任把裝置對象放到裝置堆棧上,從PDO開始一直向上。可以調用IoAttachDeviceToDeviceStack完成:

NTSTATUS AddDevice(..., PDEVICE_OBJECT pdo)

PDEVICE_OBJECT fdo;

IoCreateDevice(..., &fdo);

pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);

清除DO_DEVICE_INITIALIZING标志(在WDM驅動中)。

[1] Windows驅動程式模型設計

[3] 驅動程式開發書籍

•Art Baker,《The Windows NT Device Driver Book: A Guide for Programmers》,(Prentice Hall, 1997)。

•Chris Cant,《Writing Windows WDM Device Drivers》,(R&D Press, 1999)。

•Edward N. Dekker和Joseph M. Newcomer,《Developing Windows NT Device Drivers: A Programmer's Handbook》,(Addison-Wesley, 1999)。

•Rajeev Nagar,《Windows NT File System Internals: A Developer's Guide》,(O'Reilly & Associates, 1997)。

•Peter G. Viscarola和W. Anthony Mason,《Windows NT Device Driver Development》,(Macmillan, 1998)。

其它參考書籍

•David A. Solomon,《Inside Windows NT, Second Edition》,(Microsoft Press, 1998)。