定義
Intel x86
體系結構的處理器定義了4個級別的許可權(稱為Ring),Windows系統使用了
Ring0(供特權模式使用)和Ring3(供用戶模式使用),Windows系統只使用了2個級別的許可權級別的原因是為了和其他一些
硬體系統兼容,這些硬體系統只有2個級別的許可權,如Compaq Alpha和Silicon Graphics MIPS等。
每個用戶模式的進程有其私有的
地址空間,這些進程在最低的許可權級別下運行(稱為Ring3或者用戶模式),它們不允許執行CPU的
特權指令,對系統所屬的數據、地址空間以及硬體等的訪問也是被嚴格限制的,例如,如果某個
用戶程式訪問4G地址空間中的高位2G,那么系統就會立即將其終止執行。要注意的是,進程調用系統功能的時候,可以切換到
核心模式執行,但是調用結束後,就返回到用戶模式了。
用戶模式的進程總是被認為是對作業系統穩定性的潛在威脅,所以它們的許可權被嚴格地限制,任何觸及這些限制的舉動都將使進程被終止。
而核心模式的組件則可以共享這些受保護的核心模式記憶體空間,在特權級別下運行(也稱為
Ring0),允許執行任何CPU指令,包括
特權指令,可以無限制地訪問系統數據、代碼和硬體資源。
核心模式代碼運行在
系統地址空間中,並總是被認為是可信任的,一旦被裝載運行後,
驅動程式就是系統的一部分,可以無限制地做任何事情。
總的來說,用戶模式程式被完全從作業系統隔離,這對作業系統的完整性來說是件好事情,但對某些種類的應用程式來說就太頭痛了,比如Debug工具。幸運地是,這些在用戶模式幾乎不可能完成的任務完全可以通過核心模式的驅動程式來完成,因為這些驅動程式的操作是不受限制的。因此,如果你打算從用戶模式存取作業系統內部的
數據結構或者函式的話,唯一的方法就是將一個
核心模式驅動程式裝載到系統的
地址空間中(並調用它),這是很簡單的事情,作業系統完全支持這樣的操作。
主要組成部分
根據
地址空間、代碼許可權和職責的不同,Windows NT內部劃分為兩個截然不同的部分。
地址空間的享用方式也
非常容易理解,整個32位系統的4GB內容被劃分為兩個相等的部分,用戶模式(user-mode)的進程使用的地址空間被映射到低位的2GB上(地址範圍00000000 - 7FFFFFFFh),而高位的2GB(地址範圍80000000h - 0FFFFFFFFh)則供作業系統的組成部分來使用,如設備
驅動程式、系統記憶體池、系統使用的
數據結構等,在這部分中,
記憶體共享的許可權和職責等方面就要複雜一點了。
下面就是用戶模式進程的一些簡單分類:
◎ 用戶應用程式--任何Win32、Windows 3.1、DOS、
POSIX或者
OS/2程式
◎ 子系統--Windows內置3個子系統:Win32(位於\%SystemRoot%\System32\
Csrss.exe)、POSIX子系統(位於\%SystemRoot%\System32\
Psxss.exe)和OS/2子系統(位於\
%SystemRoot%\
System32\Os2ss.exe),在Windows XP以及後續的作業系統中,POSIX和OS/2子系統已經被去掉了。
◎ 設備
驅動程式--硬體設備驅動程式、檔案系統和網路驅動程式
◎ 視窗和
圖形系統--實現GUI函式,如處理視窗、用戶界面的控制和繪畫等(位於\%SystemRoot%\System32\Win32k.sys)
分類
Windows NT支持的設備驅動程式的範圍很廣,它們的分類如下:
◎
虛擬設備驅動程式(Virtual Device Drivers/VDD)--用戶模式的組件,用於為16位的MS-DOS應用程式提供虛擬的執行環境,雖然和Windows 95/98裡面的
VxD從功能上看起來是差不多的,但實際上兩者根本不同。
◎ 檔案系統驅動程式--實現標準的檔案系統模型
◎ 傳統設備
驅動程式--用於在沒有其他驅動程式幫助的情況下控制硬體設備,它們是為老版本的Windows NT系統所寫的,但是也可以不加修改地運行在Windows 2000/XP/2003系統上
◎ WDM驅動程式--即Windows Driver Model,
WDM包括對Windows NT
電源管理和
即插即用的支持,WDM可以在Windows 2000、Windows 98和Windows ME下實現,所以在這些作業系統下,WDM驅動程式在原始碼級別是兼容的,在有些情況下,在
二進制代碼級別上也是兼容的
在不同的資料中,對
驅動程式的分類方法可能完全不同,但這並不是問題。
從名稱理解,設備驅動程式是用於控制某個設備的,但這個"設備"並不一定指的是物理上存在的設備,它也可以是
虛擬設備。
從檔案結構上講,設備驅動程式就是一個普普通通的
PE格式檔案,就像其他EXE或者
DLL檔案一樣。設備驅動程式是一個可裝載的
核心模式模組,一般以SYS為擴展名。他們之間的不同點在於兩種的裝載方法是完全不同的。實際上,我們可以把設備驅動程式理解成一個核心模式的DLL,用於完成在用戶模式下所不能完成的功能,本質上的不同就在於我們無法直接存取設備驅動程式的代碼和數據(註:DLL的代碼和數據是可以被直接存取的,這方面的資料可以參考《Windows環境下32位彙編語言程式設計一書》中的DLL一章),唯一的存取方式是通過I/O管理器,它提供了簡單的
驅動程式管理環境。
剛開始學習KMD的開發的時候,你可能感覺自己根本就是一個菜蟲(旁白:就是比菜鳥還低級,呵呵~~~),因為你以前用Windows API開發程式的經驗在這裡根本幫不上忙,即使你以前寫過n多個(n趨向
無窮大……)用戶模式下的套用系統也沒用。核心提供了完全不同的函式和
數據結構,以至於你要從頭開始了解,而且資料奇缺無比,一般情況下,可供參考的只有頭檔案。
驅動程式
大部分控制硬體設備的驅動程式是分層的驅動程式,分層驅動的概念就是當用戶模式發出一個請求時,每個請求從高層次的驅動程式逐層處理並流傳到低層次的驅動程式中,一個I/O請求的處理可能分步在多個驅動程式中,例如,如果一個應用程式發出讀盤請求,處理請求會在多個驅動程式中流過,在其中你也可以再加入n多個過濾驅動程式(比如插入一個加解密的模組)。
單層的
驅動程式是最簡單的一類驅動程式,這一類驅動程式通常並不依賴於其他已裝載的驅動程式,他們的接口僅僅針對用戶模式的應用程式,開發和調試這一類驅動程式是非常簡單的,我們即將開始討論的就是這類程式,其他類型的驅動程式將在以後討論。
在大多數情況下,我們的系統中只安裝了一個CPU,所以,對於所有這些運行中的程式來說,作業系統對每個進程中的
執行緒所使用的
CPU時間進行調度,循環為每個執行緒分配
時間片,這就造成了多個程式同時執行的假象。如果系統中安裝了多個CPU,那么作業系統的
調度算法將複雜得多,因為它要將各CPU上的執行緒進行平衡。如果Windows檢測到一個新執行緒要開始運行了,它將進行一次
上下文切換(context switch)(註:上下文(Content)實際上就是執行緒運行的環境,也就是運行時各
暫存器和其他東東的狀態,更自然的理解就是"執行緒狀態")。所謂上下文切換就是保存
執行緒運行時的機器狀態,然後將另一個執行緒的狀態恢復並重新開始執行。如果重新開始執行的執行緒屬於另一個進程,那么該進程的
地址空間也將被同時切換過來(通過在CR3暫存器中裝入
頁表)。
每個用戶進程都有私有的地址空間,所以他們的頁表都是不同的,CPU通過切換頁表來將
虛擬地址映射到物理地址,設備
驅動程式並不需要直接做這些工作。
上下文切換比較耗
CPU時間,所以驅動程式一般不創建它們自己的執行緒,它們一般在下列環境中的一個中運行:
3. 作為中斷運行(並不處於哪個特定的進程或
執行緒中,因為它們都被暫時掛起了)
在處理
I/O請求包(IRPs)時,我們總是運行在和用戶模式的調用者相同的
進程上下文中運行,這樣我們就能對
用戶程式的
地址空間進行定址。但是當驅動程式被載入或者卸載的時候,我們將在
系統進程中運行,這時存取的只能是系統的地址空間。
中斷是任何作業系統都少不了的組成部分,中斷使處理器打斷正常的程式流程來首先處理它們,中斷分
硬體中斷和軟體中斷兩種,中斷是分優先權的,一個高優先權的中斷可以打斷低優先權的中斷的執行。
Windows中把
中斷優先權稱為IRQLs(interrupt request levels),在系統中表示為從0(被動)到31(高級)的整數,其中大的數值對應高
優先權的中斷。注意
IRQL值的含義和
執行緒調度優先權的含義是完全兩碼事情。
嚴格來說,IRQL=0的中斷並不是中斷,因為它無法打斷任何其他代碼的執行(因為沒有比0更低級的代碼了),所有的用戶模式
執行緒在這個級別上運行,該級別也稱為被動級別(passive level)。我們後面要討論的
驅動程式代碼也在這個級別上運行,注意這並不意味著其他的驅動程式也在被動級別下運行。
因此這裡還有兩個重要的結論:
首先:當驅動程式運行於用戶模式程式的執行緒中時,代碼的執行可能被高IRQL級別的代碼打斷,一些函式可以用來獲取當前的IRQL值,並可以對其進行提升或者降低。
第二:被動模式
IRQL下的代碼可以調用任何的核心函式(
DDK指明了每個函式允許調用的IRQL級別),可以對已
分頁的或未分頁的記憶體進行
定址(註:即已映射過的
虛擬地址還是
物理記憶體地址)。反過來,當在一個比較高的IRQL級別下對分頁記憶體進行定址時(指等於或高於DISPATCH_LEVEL),系統將崩潰,因為這時
記憶體管理器的IRQL級別反而比較低,以至於無法處理頁錯誤了。
我想每個人都見過著名的藍屏
當機畫面,即"Blue Screen Of Death",簡稱為
BSOD,也許根本不需要解釋它是怎么出現或者在什麼時候出現的,因為在後面的KMD開發過程中,你會很頻繁地遇到它們。
在
核心模式下,Windows不對任何
系統記憶體進行保護,由於核心模式的
驅動程式可以對系統記憶體和作業系統的
地址空間進行任意存取,所以你必須對你開發的驅動程式進行嚴格的測試,以防它危及到系統的穩定。
你可以把這個作為最基本的原則,另外,如果沒有執行緒上下文、
中斷優先權、核心模式和用戶模式等方面的概念,開發核心模式驅動程式將是不可能的事(天哪,到現在我才發現,我連菜蟲都算不上,我竟然是~~~~~~菜菜的
單細胞生物!嗚嗚~~)
Windows DDK是
MSDN專業版和宇宙版的一部分,它也可以從microsoft.com ddk 下載,對於開發設備
驅動程式來說,
DDK是關於Windows NT內部信息,包括
系統函式、
數據結構等的豐富資源,不幸的是,
微軟已經停止了免費發放DDK,所以現在只好去買正版的CD了(沒有槍,沒有炮,盜版游擊隊給我們造~~~)
除了文檔,DDK還包含了一堆的庫檔案(*.lib),這些庫可以在連結的時候用上。這些庫有兩種版本:普通的版本(稱為free build)和特殊的包含Debug信息的版本(稱為checked build),它們分別位於%ddk%\libfre\
i386和%ddk%\libchk\i386目錄下,check build是在編譯Windows
原始碼時加上
DEBUG標誌後生成的,在開發
驅動程式時,它們可以提供更加精確的錯誤定位,但是你首先要根據你的作業系統選擇合適的lib版本才行。
KmdKit包含了所有用彙編開發KMD所需要的東西:include檔案、
lib檔案、
宏定義、例子檔案、工具和一些文章,你可以自己在
軟體包中找到更多的東西,下一節我們將從這個軟體包中包括的一些例子開始學習KMD的編程。
調試
核心模式的代碼需要合適的調試器,Compuware的
SoftIce是個不錯的選擇(見compuware.com products numega index.htm),當然你也可以使用Microsoft Kernel Debugger,它需要兩台計算機:主機和目標機器,目標機器是被調試的機器,主機是運行調試軟體的機器。Mark Russinovich ( sysinternals.com ) 也寫了一個工具,叫做LiveKd,它允許在單台機器上運行Microsoft Kernel Debugger,而不再需要兩台機器了。