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

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