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