內核模式編程環境
圖3-1顯示了Windows NT操作系統的某些組成部分。每個部分都輸出一些服務函數,這些函數以兩個特別的字母組合開頭:
- I/O管理器(Io前綴) 包含許多驅動程序可以使用的服務函數,對這些函數的描述遍及本書。
- 進程結構模塊(Ps前綴) 創建并管理內核模式線程。普通的WDM驅動程序應使用一個獨立的線程來循檢無中斷生成能力的設備。
- 內存管理器(Mm前綴) 控制頁表,頁表定義了虛擬內存到物理內存之間的映射。
- executive (Ex前綴) 提供堆管理和同步服務。本章將討論堆管理函數。下一章討論同步服務。
- 對象管理器(Ob前綴) 集中控制Windows NT中的各種數據對象。WDM驅動程序僅需要對象管理器維護對象的參考計數,以防止對象被意外刪除。
- 安全參考監視器(Se前綴) 使文件系統驅動程序執行安全檢測。I/O請求到達WDM驅動程序前已經做完了安全檢測,所以本書不討論這些函數。
- 運行時間庫部件(Rtl前綴) 包含工具例程,例如列表和串管理例程,內核模式驅動程序可以用這些例程來替代常規的ANSI標準例程。大部分例程可以從其名字上直接看出它的功能。
- Win32子系統存在于用戶模式中,所以用戶模式中的應用程序可以容易地調用其例程。為了方便,Windows NT在內核模式中實現了一些有Zw前綴名的函數,這些函數可以使驅動程序調用Win32子系統例程。Windows 2000 DDK中僅暴露一少部分這樣的函數給驅動程序使用,包括訪問文件和注冊表的函數。我將在本章討論這些函數。
- Windows NT內核(Ke前綴) 所有多線程和多處理器的低級同步活動都發生在內核中,我將在下一章中討論KeXxx函數。
- 在操作系統的最底層是硬件抽象層(HAL,Hal前綴)。操作系統把所有關于計算機硬件如何連接的信息都存放在HAL中。HAL了解如何在特定平臺上實現中斷操作,如何實現自旋鎖,如何尋址I/O或內存映射設備,等等。 WDM驅動程序不直接與硬件對話,它通過調用HAL中的函數來達到目的。所以WDM驅動程序能夠實現平臺無關和總線無關。

圖3-1. 內核模式支持例程概觀
使用標準運行時間庫函數
在歷史上,Windows NT的設計者認為,不應該在驅動程序中使用C編譯器廠商提供的運行時間庫。部分原因是由于Windows NT是在ANSI標準出臺前設計的,每個C編譯器廠商都有自己的實現方法和品質標準。另一個原因是因為標準運行時間庫中的例程有時需要依賴用戶模式中的應用程序來初始化,并且有些例程并不是以多線程或多處理器安全的方式實現的。
直到現在,官方認為內核模式驅動程序僅應調用DDK中公開的函數。例如,你不能在驅動程序中調用wcscmp函數,而應該調用RtlCompareUnicodeString。然而,這里有一個公開的秘密,用于創建驅動程序的標準輸入庫(ntoskrnl.lib)定義了許多函數,而這些函數卻是在諸如string.h、stdio.h、stdlib.h,和ctypes.h的頭文件中聲明的,這些頭文件都是應用程序經常使用的頭文件。所以,為什么我們不能使用它們?實際上,倘若你了解所有的內部細節,你完全可以調用它們。但你不能總這樣做,例如,你不能總用memcpy替代RtlCopyBytes,因為這兩者稍有不同。(RtlCopyByte可以保證一個字節一個字節地復制數據而不是以較大的塊,大塊復制數據在某些RISC平臺上會出現麻煩)
注意側效
驅動程序中使用的許多支持“函數”其實是DDK頭文件中定義的宏。我們都知道應該避免在宏的參數中使用帶有邊效的表達式,原因很明顯,宏可以多次使用其參數,見下面代碼:
int a = 2, b = 42, c;
c = min(a++, b);
|
a的值是什么?(c的值又是什么?) 讓我們看看這個似是而非的min宏:
#define min(x,y) (((x)<(y)) ? (x) : (y))
|
如果你用a++代替x,你將看到a最后等于4,因為表達式a++執行了兩次。而“函數”min將返回3而不是2,因為函數的返回值是在第二次計算a++之前提取的a值。
通常,你不能知道DDK什么時候使用宏,什么時候使用真正的外部函數。有時候,一個特殊的服務函數在某些平臺上是宏而在其它平臺上卻是外部函數。此外,Microsoft也可能在將來改變想法。所以,當你寫WDM驅動程序時應堅守下面原則:
決不在內核模式服務函數的參數中使用帶有側效的表達式。