- 相關(guān)推薦
Windows2000設備驅動(dòng)程序的研制開(kāi)發(fā)
引言:
由于工作關(guān)系,我經(jīng)常涉及PC機與外圍設備接口的工作,從PC機這方面要做的工作看來(lái),主要是通過(guò)接口處理外圍設備的中斷,通過(guò)I/O端口或內存地址與外設互相傳遞數據。從計算機原理的角度看,所要達到的目的很簡(jiǎn)單,那么如何編寫(xiě)程序完成上述功能呢?
目前國內流行的PC操作系統有三種:DOS,Win95/98系列,WindowsNT。DOS是單用戶(hù)、單任務(wù)操作系統,由于PC機硬件處理速度不斷提高,基于單用戶(hù)、單任務(wù)的操作系統越來(lái)越不能充分發(fā)揮硬件的功能,現在只應用于一些老式PC及其它個(gè)別場(chǎng)合,有逐漸被淘汰的趨勢;Win95/98系列和WindowsNT屬于多任務(wù)操作系統,不論從其原理還是界面上看,這兩種操作系統都比DOS有著(zhù)無(wú)可比擬的優(yōu)越性,這兩種操作系統雖然在界面和操作上及其相似,但其內部實(shí)現的諸多方面有許多區別,有些區別是本質(zhì)上的。Win95/98設計目標是針對一般家庭用戶(hù),安全性及可靠性存在許多薄弱環(huán)節,就可靠性而言,Win95/98系列不能很好的防止多任務(wù)環(huán)境中某個(gè)進(jìn)程的非法操作導致系統中其它程序甚至整個(gè)系統的崩潰,而WindowsNT在這方面及其它諸多方面設計的相當嚴謹。這兩種操作系統是Microsoft公司同一時(shí)期的產(chǎn)品,但針對不同的使用群,所以在一些重要場(chǎng)合及生產(chǎn)實(shí)踐中應該選擇WindowsNT作為計算機的操作系統,此外,從發(fā)展趨勢來(lái)看,WindowsNT已經(jīng)成為定型產(chǎn)品,具有相對穩定性。
在不同操作系統下編寫(xiě)驅動(dòng)程序是有很大區別的,在DOS平臺上,應用程序和設備驅動(dòng)程序之間沒(méi)有標準的接口,它們在外部表現為一個(gè)擴展名為EXE的文件,驅動(dòng)程序的作用被柔和在應用程序中,這樣,應用程序為了使用不同廠(chǎng)商的同一類(lèi)設備,必須了解這些設備在接口上具體的硬件實(shí)現,同時(shí),對于一個(gè)特定型號的硬件產(chǎn)品,所有支持它的應用軟件中對于控制整個(gè)設備動(dòng)作的這部分代碼,可能被多次重寫(xiě)。這種情況不適應硬件及應用軟件的飛速發(fā)展。Windows系統在這方面,進(jìn)行了根本性改進(jìn),把控制設備動(dòng)作的這部分代碼獨立出來(lái),提出了設備驅動(dòng)程序的概念,驅動(dòng)程序是應用程序和硬件設備之間的一個(gè)橋梁,應用程序與驅動(dòng)程序之間有明確的接口,應用程序通過(guò)與驅動(dòng)程序交換信息,達到控制外設的目的。接口定義的操作是面向設備的,這就是說(shuō),在應用程序的設計中,并不用關(guān)心對外設操作的具體硬件實(shí)現,只是對驅動(dòng)程序發(fā)出一系列指令既可;驅動(dòng)程序接受來(lái)自上層應用程序的指示,具體操縱實(shí)際硬件,完成用戶(hù)功能。具體實(shí)現上,Win95/98系列與WindowsNT又有所區別,WindowsNT是嚴格按照上述思路設計的;而Win95/98系列不那么嚴格,其支持上述思路,但同時(shí)應用程序也可以繞過(guò)驅動(dòng)程序直接訪(fǎng)問(wèn)實(shí)際物理I/O,這樣做,增加程序設計的靈活性,但同時(shí),對系統可靠性造成一定隱患。這也正是Win95/98系列可靠性低于WinNT的原因之一。
表1-1 三種操作系統下訪(fǎng)問(wèn)接口比較
操作系統應用程序訪(fǎng)問(wèn)接口方式訪(fǎng)問(wèn)權限
DOS 直接訪(fǎng)問(wèn)所有[注]
Windows95/98 通過(guò)設備驅動(dòng)程序*.VXD 所有[注]
直接訪(fǎng)問(wèn)僅I/O端口
WindowsNT 通過(guò)設備驅動(dòng)程序*.SYS 所有[注]
[注]‘所有’指I/O端口,RAM總線(xiàn),中斷,DMA。
WindowsNT設備驅動(dòng)程序的組成原理
WindowsNT操作系統結構分為用戶(hù)模式和內核模式,用戶(hù)模式下的編程為應用程序的設計,而開(kāi)發(fā)設備驅動(dòng)程序,則屬于內核模式下的編程,內核模式組件包括NT Executive(ExXxx),內核(KeXxx),硬件抽象層(HalXxx)。其層次如圖2-1所示,其中NT Executive 包括幾個(gè)獨立的軟件組件,它們是系統服務(wù)接口(ZwXxx),對象管理器(ObXxx),配置管理器,進(jìn)程管理器(PsXxx),安全監視器(SeXxx),虛擬空間管理器(MemXxx),本地進(jìn)程調用,I/O管理器(IoXxx)。內核模式的系統服務(wù)并不是全部公開(kāi)的,而是提供了一系列開(kāi)發(fā)設備驅動(dòng)程序需要的函數(上文括號內為函數形式,函數手冊參見(jiàn)[2]Kernel-Mode Drivers-Reference章節),換言之,這些函數功能是所有內核模式的系統服務(wù)功能的子集。
驅動(dòng)程序由一系列相對獨立的函數組成,由I/O管理器根據需要調用這些函數,對于一個(gè)需要處理中斷的最簡(jiǎn)單的驅動(dòng)程序也需要由以下幾個(gè)函數構成:
1.DriverEntry() 運行于PASSIVE_LEVEL
驅動(dòng)程序入口點(diǎn),當驅動(dòng)程序被手動(dòng)或自動(dòng)裝入系統后,驅動(dòng)程序從這點(diǎn)開(kāi)始執行,主要用于定位硬件資源,建立指向其它驅動(dòng)程序函數的指針等其它初始化工作。
2.XxUnload() 運行于PASSIVE_LEVEL
用于驅動(dòng)程序從系統卸出之前,釋放由驅動(dòng)程序占用的所有系統資源。
3.XxIsr() 運行于DIRQL
中斷服務(wù)程序。
4.XxDpcForIsr() 運行于DISPATCH_LEVEL
中斷服務(wù)程序后處理程序,以排隊方執行不太關(guān)鍵代碼的執行,由于排隊機制及優(yōu)先級,不會(huì )造成代碼擁塞從而提高中斷服務(wù)程序的響應并且提高系統總體I/O吞吐率。
5.XxOpen() 運行于PASSIVE_LEVEL
處理應用程序Win32函數CreateFile()請求。
6.XxClose() 運行于PASSIVE_LEVEL
處理應用程序Win32函數CloseHandle()請求。
7.XxDispatch() 運行于PASSIVE_LEVEL
處理應用程序Win32函數DeviceIoControl()請求,通過(guò)一系列自定義命令,驅動(dòng)程序與應用程序交換特定的信息。
WindowsNT使用一個(gè)抽象化的CPU優(yōu)先級方案, IRQL代表中斷請求級,任一時(shí)刻CPU總處在某一級上,這個(gè)數越大,表示當前的任務(wù)重要性越大,如表2-1所示,從上至下IRQL越來(lái)越小。所有上述驅動(dòng)程序的函數及內核模式函數都必須運行于各自的IRQL級上,如果違反這一調用規定,會(huì )造成系統崩潰。例如,中斷服務(wù)程序(XxIsr)運行于DIRQL及上,那幺在編寫(xiě)中斷服務(wù)程序時(shí),只能調用允許在這一級運行的內核模式函數(并不是所有內核模式函數都能運行于DIRQL級)。至于每個(gè)內核模式函數運行級別的說(shuō)明,詳見(jiàn)[2]Kernel-Mode Drivers-Reference章節。
WindowsNT是一多任務(wù)系統,許多設備的驅動(dòng)程序同時(shí)存在系統中,這樣各個(gè)設備所占用的資源(中斷,I/O及RAM地址空間)很有可能沖突,如果設備驅動(dòng)程序在運行之前不進(jìn)行‘探測’而使用自己硬件設備的資源,有可能和系統內其它設備占用的資源沖突,后果不堪設想。WindowsNT通過(guò)注冊表管理硬件資源的占用信息,作為內核模式信任的組件,驅動(dòng)程序使用硬件資源之前必須遵循‘查詢(xún)-申請-使用-釋放’的原則(如圖2-2所示)。
表2-1
來(lái)源 IRQL
硬件 HIGHEST_LEVEL
POWER_LEVEL
IPI_LEVEL
CLOCK2_LEVEL
CLOCK1_LEVEL
PROFILE_LEVEL
DIRQLs(I/O設備中斷平臺相關(guān)的級數)
軟件 DISPATCH_LEVEL
APC_LEVEL
PASSIVE_LEVEL
WindowsNT設備驅動(dòng)程序的編寫(xiě)步驟與實(shí)例
現以一實(shí)際例子簡(jiǎn)要說(shuō)明設備驅動(dòng)程序的開(kāi)發(fā)步驟,本例以CINRAD天氣雷達測試卡實(shí)際應用為原型,加以簡(jiǎn)化、抽象。
第一步,了解被控設備的接口情況。
本例為一ISA卡,占用PC機9號中斷,I/O地址360H及RAM地址D0228H分別一個(gè)字空間。
第二步,確定驅動(dòng)程序的功能。
驅動(dòng)程序每當9號中斷達到時(shí),檢查運行標志變量RunFlag(為一BOOL變量),如果等于TRUE,中斷累積計數器counter(為一unsigned short變量)增一,把這個(gè)值寫(xiě)入RAM地址D0228H,再從這個(gè)地址讀出,如果讀出值等于寫(xiě)入值,把這個(gè)值寫(xiě)入I/O地址360H,這個(gè)地址的內容會(huì )驅動(dòng)板卡上的LED顯示,把寫(xiě)入值顯示出來(lái);如果讀出值不等于寫(xiě)入值,設置運行標志變量FALSE。如果運行標志變量等于FALSE,什幺也不做,返回。
第三步,定義驅動(dòng)程序與應用程序的軟件接口。
本例定義兩個(gè)接口命令:
IOCTL_IOCardA_START:應用程序設置驅動(dòng)程序內部的運行標志變量等于TRUE。
IOCTL_IOCardA_READ:應用程序查詢(xún)驅動(dòng)程序內部的中斷累積計數器的值。
第四步,畫(huà)流程圖。這里列舉本例實(shí)現的幾個(gè)主要流程圖,(圖略)。
系統傳給驅動(dòng)程序入口函數系統定義的‘設備驅動(dòng)對象’DrObj,通過(guò)初始化這個(gè)對象的一些成員變量,把驅動(dòng)程序其它函數與這個(gè)對象聯(lián)系起來(lái)。
ISA卡為非即插即用設備,事先把資源占用信息手工添加注冊表如下:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IOCardA\parameters]
"IRQ"=dword:00000009
"IOSPAN"=dword:00000004
"IOAdd"=dword:00000360
"RAMAdd"=dword:000d0228
"RAMSPAN"=dword:00000002
其中IOCardA以下各子鍵及其值為自定義,設備驅動(dòng)程序利用相應函數檢索出這些值。
(3)每個(gè)設備驅動(dòng)程序可以創(chuàng )建若干系統定義的‘設備對象’,本例根據需要只創(chuàng )建了一個(gè)‘設備對象’Dev。‘設備對象’其中一個(gè)成員變量為指向一非分頁(yè)的物理內存塊DeviceExtension,這塊內存大小及內容為用戶(hù)自定義,由于Dev或DeviceExtension對象會(huì )被系統傳給驅動(dòng)程序的其它函數,這樣驅動(dòng)程序各函數通過(guò)訪(fǎng)問(wèn)這塊內存區,實(shí)際上達到互相傳遞信息的功能。本例在這里存儲設備硬件資源信息及RunFlag和中斷計數器counter,這些數值在DriverEntry()初始化后,供驅動(dòng)程序的其它函數使用。
圖3-2為中斷服務(wù)程序IOCardAIsr()流程圖。操作系統接受中斷,連同DeviceExtension等參數傳給中斷服務(wù)程序,中斷服務(wù)程序利用這些參數,實(shí)現要求功能。
圖3-3為IOCardADispatch()流程圖,這個(gè)函數用于處理來(lái)自上層應用程序的命令。上層應用程序通過(guò)以下程序段設置驅動(dòng)程序中RunFlag值為T(mén)RUE,從而啟動(dòng)中斷服務(wù)程序開(kāi)始計數。
BOOL cmd=TRUE;
hTest = CreateFile(...); //打開(kāi)設備
DeviceIoControl(hTest, //設備句柄
IOCTL_IOCardA_START,//命令
&cmd,sizeof(BOOL), //輸入緩沖區地址及大小
NULL,0,&c,NULL);
CloseHandle(hTest); //關(guān)閉設備
上層應用程序通過(guò)以下程序段查詢(xún)當前的中斷計數器的值并存于變量w中。
unsigned short w;
hTest = CreateFile(...);
DeviceIoControl(hTest,
IOCTL_IOCardA_READ, //命令
NULL,0,
&w,sizeof(unsigned short),//輸出緩沖區地址及大小
&c,NULL);
CloseHandle(hTest);
其中DeviceIoControl()執行后,操作系統調用IOCardADispatch()函數,如流程圖所示,這個(gè)函數內部通過(guò)一個(gè)開(kāi)關(guān)語(yǔ)句,根據命令執行相應的分支。驅動(dòng)程序與應用程序通過(guò)此函數接口交換數據時(shí),操作系統提供4種可選數據緩沖方式,本例由于數據I/O量比較小,故選用‘緩沖I/O’ (METHOD_BUFFERED)。過(guò)程是,I/O管理器首先分配一個(gè)非分頁(yè)池,它的大小為調用者輸入緩沖區和輸出緩沖區的較大者,第一段程序為sizeof(BOOL),第二段程序為sizeof(unsigned short),它的地址存到IRP(I/O請求包)的AssociatedIrp.SystemBuffer域中,然后把輸入數據拷貝到這個(gè)池中,在第一段程序中cmd的值TRUE被拷貝到池中,這樣驅動(dòng)程序通過(guò)RtlCopyBytes()函數再把池中的值拷貝到驅動(dòng)程序的RunFlag中。IOCardADispatch()函數執行完,I/O管理器把池中的內容拷貝到調用者的輸出緩沖區,在第二段程序中,驅動(dòng)程序通過(guò)RtlCopyBytes()函數把counter的值拷貝到池中,從而最終傳遞到應用程序變量w中。
第五步,編程。在編寫(xiě)設備驅動(dòng)程序的同時(shí),要編寫(xiě)一個(gè)簡(jiǎn)單的應用程序用于測試設備驅動(dòng)程序的一些功能。
第六步,驅動(dòng)程序的載入。
驅動(dòng)程序C語(yǔ)言源程序經(jīng)過(guò)編譯、連接生成擴展名為SYS的文件,本例為IOCardA.sys,把這個(gè)文件拷貝到\WINNT\system32\drivers\系統目錄下,同時(shí)手工添加如下信息到注冊表:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IOCardA]
"ErrorControl"=dword:00000001
"Start"=dword:00000003
"Type"=dword:00000001
要保證IOCardA子鍵名與驅動(dòng)程序文件名一致,其中Type=1表示此驅動(dòng)程序為內核模式驅動(dòng)程序,Start=3表示此驅動(dòng)程序手動(dòng)載入,ErrorControl=1表示當驅動(dòng)程序發(fā)生錯誤時(shí),日志記錄錯誤并顯示一個(gè)消息框。這樣當Windows重新啟動(dòng)后,通過(guò)使用控制面板中的Device小應用程序,從列表中找到IOCardA設備名,按Start按鈕,于是,設備驅動(dòng)程序就駐留內存并在底層開(kāi)始工作了。
第七,調試。設備驅動(dòng)程序沒(méi)有界面,完全在系統底層運行,為了觀(guān)察驅動(dòng)程序的運行狀態(tài),WindowsNT DDK提供windbj.exe程序用于設備驅動(dòng)程序的調試,調試設備驅動(dòng)程序需要兩臺CPU體系結構完全相同的計算機,一臺為‘宿主機’,運行windbj.exe程序,另一臺為‘目標機’,運行設備驅動(dòng)程序,兩臺計算機用串口線(xiàn)連好,進(jìn)行一系列軟件設置(參見(jiàn)[1]第17章),就可以開(kāi)始調試了,從‘宿主機’可以控制及觀(guān)察‘目標機’上驅動(dòng)程序的運行情況。最常用的調試手段是在驅動(dòng)程序中必要的位置加入DbgPrint()函數,這個(gè)函數可以把指定信息輸出到‘宿主機’windbg.exe窗口中,通過(guò)分析這些信息,可以了解驅動(dòng)程序當前的運行情況。
結束語(yǔ)
WindowsNT是一個(gè)復雜而嚴密的系統,驅動(dòng)程序的開(kāi)發(fā)不可避免的涉及現代操作系統理論及其它許多計算機理論,內涵相當廣泛,本文圍繞著(zhù)開(kāi)發(fā)實(shí)踐從一定深度探討了WindowsNT設備驅動(dòng)程序開(kāi)發(fā)最基本的知識及一般方法,希望對讀者有所幫助,對于復雜,特殊應用的實(shí)現及編程技巧,有待于讀者在各自實(shí)際應用中不斷探索。
參考文獻
1.《WindowsNT設備驅動(dòng)程序設計指南》 Art Baker著(zhù) 機械工業(yè)出版社
2.Microsoft Co. WindowsNT 4.0 Device Driver Kid
【W(wǎng)indows設備驅動(dòng)程序的研制開(kāi)發(fā)】相關(guān)文章:
基于Windows2000開(kāi)發(fā)WDM設備驅動(dòng)程序的方法03-19
Windows CE中實(shí)現藍牙串口驅動(dòng)程序03-18
windows nt環(huán)境下fddi網(wǎng)卡驅動(dòng)程序設計03-18
對于設備驅動(dòng)程序通知應用程序的幾種方法11-16
Windows 中斷程序設計03-28