久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux和Windows設(shè)備驅(qū)動架構(gòu)比較

 昵稱31851752 2016-07-26

      畢業(yè)后一直在學(xué)操作系統(tǒng),, 有時候覺得什么都懂了,,有時候又覺得好像什么都不懂,但總體來說自認(rèn)為對操作系統(tǒng)實現(xiàn)機制的了解比周圍的人還是要多一些,。去年曾花了幾個星期的晚上時間斷斷續(xù)續(xù)翻譯了這篇對Linux和Windows驅(qū)動架構(gòu)進(jìn)行比較的論文,。原文在這里


LinuxWindows設(shè)備驅(qū)動架構(gòu)比較

1. 概述

這篇論文中,,我們將考查目前最為廣泛使用的兩種操作系統(tǒng),,即LinuxWindows系統(tǒng)的設(shè)備驅(qū)動架構(gòu)。為每種操作系統(tǒng)實現(xiàn)設(shè)備驅(qū)動所需要的驅(qū)動組件將被展示并進(jìn)行比較,,同時也展示每種操作系統(tǒng)中執(zhí)行I/O到內(nèi)核緩沖的驅(qū)動的實現(xiàn)過程。最后將以對每種操作系統(tǒng)為開發(fā)者所提供的開發(fā)環(huán)境和輔助設(shè)施的考查收尾,。

 

2. 引言

現(xiàn)代操作系統(tǒng)中包含多個模塊,,諸如內(nèi)存管理器、進(jìn)程調(diào)度器,、硬件抽象層和安全管理器,。欲了解Windows內(nèi)核的細(xì)節(jié),可參考[Russinovich, 98],,Linux內(nèi)核則參考[Rusling, 99], [Beck et al, 98],。內(nèi)核可被看作一個黑盒,并且它應(yīng)該知道如何與現(xiàn)存的不同種類的以及還沒出現(xiàn)的更多硬件設(shè)備進(jìn)行交互,。實現(xiàn)一個內(nèi)核,,使其能夠與所有被熟知的硬件設(shè)備進(jìn)行交互是可能的,但并不現(xiàn)實,,因為這會消耗太多的系統(tǒng)資源,,沒有必要。

內(nèi)核模塊化

在內(nèi)核被創(chuàng)建的時候,,并不期望它能知道如何與未出現(xiàn)的設(shè)備進(jìn)行交互?,F(xiàn)代操作系統(tǒng)內(nèi)核允許通過在運行時添加設(shè)備驅(qū)動模塊的方式來擴展系統(tǒng)功能,這個模塊的功能使得內(nèi)核可以與某種特定的新設(shè)備進(jìn)行交互,。每個模塊都提供一個例程供內(nèi)核在模塊被加載時進(jìn)行調(diào)用,,還有一個例程在模塊被移除時調(diào)用。每個模塊還實現(xiàn)各種不同的例程以便實現(xiàn)將數(shù)據(jù)傳送到設(shè)備或從設(shè)備接收數(shù)據(jù)的I/O功能,,同時還有一個例程供發(fā)送I/O控制指令到設(shè)備,。以上所述對LinuxWindows兩種驅(qū)動架構(gòu)均適用。

本論文的組織結(jié)構(gòu)

本論文分成下面幾節(jié):

l 兩種操作系統(tǒng)大致的驅(qū)動架構(gòu)(第二節(jié))

l 每種操作系統(tǒng)驅(qū)動架構(gòu)組件(第三節(jié))

l 實現(xiàn)一個驅(qū)動執(zhí)行I/O到內(nèi)核緩沖(第四節(jié))

l 兩種操作系統(tǒng)為開發(fā)者提供的驅(qū)動開發(fā)環(huán)境和輔助設(shè)施(第五節(jié))

相關(guān)工作

Windows設(shè)備驅(qū)動架構(gòu)相關(guān)文檔可在Windows驅(qū)動開發(fā)包中找到,,更有甚者,,Walter Oney [Oney, 99] 和Chris Cant [Cant, 99] 對Windows驅(qū)動架構(gòu)進(jìn)行了詳細(xì)的展示。Linux設(shè)備驅(qū)動架構(gòu)則由Rubini et al [Rubini et al,01]作了很好的描述,可免費獲取,。

 

3. 設(shè)備驅(qū)動架構(gòu)

設(shè)備驅(qū)動通過暴露編程接口的方式使得應(yīng)用程序和操作系統(tǒng)可以對設(shè)備進(jìn)行控制來達(dá)到對硬件的操作,。這一節(jié)將展示當(dāng)前最常用的兩個操作系統(tǒng),即WindowsLinux的驅(qū)動架構(gòu),,以及這些架構(gòu)的起源,。

Linux驅(qū)動架構(gòu)的起源

Linux可以說是Unix操作系統(tǒng)的一個克隆,首先由Linus Travolds創(chuàng)造 [Linus FAQ, 02], [LinuxHQ,02],。Linux沿用了類似于Unix的系統(tǒng)架構(gòu),。Unix系統(tǒng)將設(shè)備看作是文件系統(tǒng)的節(jié)點。設(shè)備以特殊文件節(jié)點的方式呈現(xiàn)在目錄中,,該目錄通常包含設(shè)備文件系統(tǒng)的節(jié)點入口[Deitel, 90],。用文件系統(tǒng)節(jié)點來表示設(shè)備的目的是使得應(yīng)用程序能以設(shè)備無關(guān)的方式訪問各種設(shè)備[Massie, 86],[Flynn et al, 97]。應(yīng)用程序仍然可以通過I/O控制操作進(jìn)行特定于設(shè)備的操作,。設(shè)備由主設(shè)備號和次社保號進(jìn)行標(biāo)識,。主設(shè)備號用來作為驅(qū)動數(shù)組的索引下標(biāo),而次設(shè)備號將相似的物理設(shè)備歸組[Deitel, 90],。Unix有兩種類型的設(shè)備,,即字符設(shè)備和塊設(shè)備。字符設(shè)備驅(qū)動管理沒有緩沖并需要順序訪問的設(shè)備,,塊設(shè)備驅(qū)動管理那些可隨機訪問的設(shè)備,,數(shù)據(jù)以塊的方式被訪問。另外,,塊設(shè)備驅(qū)動還用到緩沖區(qū),。塊設(shè)備必須以文件系統(tǒng)節(jié)點的方式掛載之后才能被訪問[Beck et al, 98]。Linux保留了Unix的很多架構(gòu)設(shè)計,,區(qū)別在于,,Unix系統(tǒng)中,每個塊設(shè)備需要創(chuàng)建一個對應(yīng)的字符設(shè)備,,而在Linux中,,虛擬文件系統(tǒng)(VFS)接口使得字符設(shè)備和塊設(shè)備的區(qū)分變得模糊[Beck et al, 98]。Linux還引入了第三種設(shè)備,,叫網(wǎng)絡(luò)設(shè)備,。訪問網(wǎng)絡(luò)設(shè)備驅(qū)動的方式和訪問字符設(shè)備、塊設(shè)備的方式不同,,它使用了不同于文件系統(tǒng)I/O接口的一個接口集,。比如socket接口,就是用來訪問網(wǎng)絡(luò)設(shè)備的,。

Windows驅(qū)動架構(gòu)的起源

1980年,,微軟從貝爾實驗室獲取Unix操作系統(tǒng)的許可,,之后作為XENIX操作系統(tǒng)發(fā)布。1981年,,MS DOS第一版隨著IBM PC發(fā)布,,并具有和基于XENIXUnix系統(tǒng)類似的驅(qū)動架構(gòu)[Deitel, 90]。和Unix操作系統(tǒng)不同的是,,這個系統(tǒng)內(nèi)嵌了常規(guī)設(shè)備所需的驅(qū)動程序,。設(shè)備入口不以文件系統(tǒng)節(jié)點的形式呈現(xiàn),而是給設(shè)備賦予預(yù)留的名稱,,如CON表示鍵盤或屏幕,,PRN表示打印機,AUX表示串口,。應(yīng)用程序不能像對待文件系統(tǒng)節(jié)點那樣通過打開設(shè)備獲取和驅(qū)動程序關(guān)聯(lián)的設(shè)備句柄從而對設(shè)備進(jìn)行I/O操作,。操作系統(tǒng)透明地將預(yù)留的設(shè)備名稱對應(yīng)到驅(qū)動程序所管理的設(shè)備。MS DOS第二版引進(jìn)了可加載驅(qū)動的概念,。由于Microsoft公開了驅(qū)動架構(gòu)的接口,,這也促進(jìn)了第三方設(shè)備制造商生產(chǎn)更多設(shè)備 [Davis, 83]。硬件制造商可以為這些新設(shè)備提供運行時可加載到內(nèi)核或從內(nèi)核移除的驅(qū)動程序,。

 

之后,Microsoft又發(fā)布了Windows 3.1,,它支持更多的設(shè)備并使用基于MS DOS的架構(gòu),。之后的Windows 9598NT,,Microsoft引入了WDM(Windows Driver Mode),。WDM的出現(xiàn)是因為Microsoft想要驅(qū)動程序代碼和后面所有新的操作系統(tǒng)兼容[Microsoft WDM, 02]。因此,,驅(qū)動遵守WDM規(guī)范的好處是,,驅(qū)動程序只需編寫一次,在Microsoft之后所有新版操作系統(tǒng)上使用時只需要重新編譯該驅(qū)動即可,。

Windows驅(qū)動架構(gòu)

Windows驅(qū)動分兩類,,分別為遺留驅(qū)動和即插即用驅(qū)動。這里我們只將重點放在PnP驅(qū)動上,,所有提及的驅(qū)動都大可認(rèn)為是PnP驅(qū)動,。PnP驅(qū)動不需費什么力氣就能安裝好,因此它對用戶是友好的,。另外一個使驅(qū)動程序支持PnP的好處是它們只會在需要的時候被操作系統(tǒng)加載,,因此它們不會無端的耗盡系統(tǒng)資源。遺留驅(qū)動是為Microsoft早期的操作系統(tǒng)實現(xiàn)的,,它們的架構(gòu)已經(jīng)過時,。WDMMicrosoft指定的標(biāo)準(zhǔn)驅(qū)動模型[Microsoft DDK, 02],。WDM驅(qū)動適用于Microsoft近期所有的操作系統(tǒng)(Windows 95和之后的)

WDM驅(qū)動架構(gòu)

WDM驅(qū)動分三類,,分別為過濾驅(qū)動,、功能驅(qū)動和總線驅(qū)動[Oney, 01]。它們形成了圖2.3所示的棧式結(jié)構(gòu),。另外,,WDM驅(qū)動必須是具有PnP感知的,支持電源管理和Windows管理規(guī)范(Windows Management Instrumentation),。圖2.3顯示了各個驅(qū)動如何交互數(shù)據(jù)和消息,。一個叫I/O請求包(IRPI/O Request Package)的標(biāo)準(zhǔn)結(jié)構(gòu)被用來進(jìn)行通信,。任何時候,,應(yīng)用程序向驅(qū)動程序發(fā)送請求時,I/O管理將創(chuàng)建IRP并下傳到驅(qū)動程序,,驅(qū)動程序處理完畢后,,“完成”這個IRP [Cant, 99]。不是所有的IRP都被下發(fā)到總線驅(qū)動,,有些IRP被上層的驅(qū)動處理后直接返回到I/O管理器,。對設(shè)備硬件的訪問需要通過硬件抽象層。

 

Figure 2.3 The WDM Driver Architecture

 

Linux驅(qū)動架構(gòu)

Linux下的驅(qū)動以模塊的形式呈現(xiàn),,這些模塊就是擴展了Linux內(nèi)核功能的一個個代碼塊[Rubini et al, 01],。模塊可形成圖2.4那樣的層次結(jié)構(gòu)。模塊之間的通信通過函數(shù)調(diào)用實現(xiàn),。模塊在加載時,,將導(dǎo)出模塊中所有對Linux內(nèi)核所維護(hù)的符號表公開的函數(shù),之后這些函數(shù)對所有的內(nèi)核模塊都是可見的,。對設(shè)備的訪問需要通過硬件抽象層,,硬件抽象層的實現(xiàn)依賴于內(nèi)核編譯時所針對的硬件平臺,如x86SPARC,。

 

Figure 2.4 The Linux Driver Architecture

 

Linux和Windows驅(qū)動架構(gòu)的比較

如圖2.32.4所示,,兩個操作系統(tǒng)有很多的相似之處。兩個系統(tǒng)中,,驅(qū)動程序都是作為擴展內(nèi)核功能的模塊化組件,。在Windows系統(tǒng)中,驅(qū)動層級之間的通信是通過將IRP作為標(biāo)準(zhǔn)系統(tǒng)函數(shù)或驅(qū)動程序自定義函數(shù)的參數(shù)來實現(xiàn),,Linux下函數(shù)調(diào)用的參數(shù)則是根據(jù)具體的驅(qū)動而不同,。Windows有單獨的內(nèi)核模塊來管理PnPI/O和電源,,這些組件在適當(dāng)?shù)臅r候?qū)?/span>IRP發(fā)送到驅(qū)動程序,。

Linux系統(tǒng)中,,模塊沒有明顯的層級關(guān)系,比如沒有區(qū)分總線,、功能,、過濾驅(qū)動。內(nèi)核沒有明確定義的PnP,、電源管理器以便在適當(dāng)?shù)臅r候?qū)⑻囟ǖ男畔l(fā)送給內(nèi)核模塊,。內(nèi)核可能會加載具有PnP、電源管理功能的內(nèi)核模塊,,但內(nèi)核模塊暴露給驅(qū)動程序的接口并沒有規(guī)定,。

這些功能一般會合并到新版的Linux內(nèi)核中,因為Linux內(nèi)核總是處于發(fā)展?fàn)顟B(tài),。每當(dāng)內(nèi)核將數(shù)據(jù)發(fā)送給棧式模塊中的某個驅(qū)動程序時,,通過這些驅(qū)動所指定的某個接口,該數(shù)據(jù)可被分享給棧式模塊中的其他驅(qū)動程序,。

在這兩種系統(tǒng)環(huán)境中,,對硬件的訪問都會通過硬件抽象層接口,硬件抽象層接口根據(jù)內(nèi)核編譯時所針對的特定平臺(X86,,SPARC)而實現(xiàn),。兩種架構(gòu)的相同之處在于,驅(qū)動程序都是運行時可加載的模塊,,每個模塊都包含一個入口點使內(nèi)核知道從哪里開始執(zhí)行模塊的代碼,。一個模塊還包含這樣一些例程,即該模塊所管理的設(shè)備接收到I/O操作請求時供內(nèi)核調(diào)用的例程,。這使得內(nèi)核可以向應(yīng)用層提供設(shè)備無關(guān)的接口。在后面的第3.3節(jié)中,,將對兩種架構(gòu)中的驅(qū)動組件作更深入的比較,。

驅(qū)動組件

編寫驅(qū)動程序時需要對硬件設(shè)備如何被操作有了解。比如說,,幾乎所有的設(shè)備都允許用戶讀取和寫入數(shù)據(jù),。這一節(jié)中將展示所有驅(qū)動程序都應(yīng)該包含的驅(qū)動組件,同時對兩種操作系統(tǒng)的驅(qū)動組件進(jìn)行比較,,并展示如何實現(xiàn)一個對內(nèi)核緩沖區(qū)進(jìn)行I/O操作的驅(qū)動程序,。本節(jié)將以對每種操作系統(tǒng)為驅(qū)動程序開發(fā)所提供的環(huán)境和輔助設(shè)施的考究來收尾。

Windows驅(qū)動組件

Windows驅(qū)動程序由各種不同的例程組成,,其中有一些是必須的,,其它的則是可選的。這一節(jié)展示所有驅(qū)動程序都必須實現(xiàn)的例程,。Windows中的設(shè)備驅(qū)動以一個叫DriverObject的結(jié)構(gòu)體表示,。用一個結(jié)構(gòu)體諸如驅(qū)動對象來表示一個驅(qū)動是有必要的,,因為內(nèi)核實現(xiàn)了可被所有驅(qū)動對象使用的各種例程。這些例程對一個驅(qū)動對象進(jìn)行操作,,這部分內(nèi)容將在下一節(jié)進(jìn)行討論,。

驅(qū)動程序初始化

Windows中每個設(shè)備驅(qū)動程序都包含一個叫DriverEntry的例程。顧名思義,,這個例程在驅(qū)動程序被加載時執(zhí)行,,驅(qū)動所管理的設(shè)備對象的初始化也在這個例程中進(jìn)行。Microsoft’s DDK [Microsoft DDK, 02] 是這樣描述的:驅(qū)動對象表示當(dāng)前被加載的驅(qū)動程序,,設(shè)備對象則表示一個物理,、邏輯或虛擬的設(shè)備。一個被加載的驅(qū)動程序(用驅(qū)動對象表示)可以管理多個設(shè)備(用設(shè)備對象表示),。初始化過程中,,設(shè)備對象中用以指定驅(qū)動程序的卸載例程、添加設(shè)備例程和分發(fā)例程都將被設(shè)置,。卸載例程用于驅(qū)動程序被卸載時做一些清除操作,,例如釋放從內(nèi)核堆中的分配的內(nèi)存。添加設(shè)備例程僅在當(dāng)驅(qū)動程序作為PnP驅(qū)動加載時,,在DriverEntry例程之后被調(diào)用,,而分發(fā)例程用于實現(xiàn)I/O操作。

AddDevice例程

PnP驅(qū)動程序需要實現(xiàn)AddDevice例程,。在這個例程中,,一個設(shè)備對象被創(chuàng)建,為該設(shè)備保存全局?jǐn)?shù)據(jù)的空間被分配,。設(shè)備資源的分配和初始化也在這里進(jìn)行,。設(shè)備對象根據(jù)其被創(chuàng)建的位置而擁有不同的名稱。如果一個設(shè)備在當(dāng)前加載的驅(qū)動程序中創(chuàng)建并用于管理該驅(qū)動,,則該設(shè)備叫功能設(shè)備對象(FDO),。如果一個設(shè)備對象是由驅(qū)動棧中位于下方的驅(qū)動程序創(chuàng)建,則該設(shè)備叫物理設(shè)備對象(PDO),。如果一個設(shè)備對象是由位于上方的驅(qū)動程序所創(chuàng)建,,則叫過濾驅(qū)動對象(FIDO)

創(chuàng)建一個設(shè)備對象

一個設(shè)備對象對應(yīng)于在AddDevice例程中調(diào)用I/O管理器中名為IoCreateDevice的例程所創(chuàng)建的設(shè)備,。對IoCreateDevice來說,,最重要的是設(shè)備對象的名稱和設(shè)備類型。這個名稱使得應(yīng)用程序和其他的內(nèi)核驅(qū)動可獲取到該驅(qū)動的句柄,,從而可進(jìn)行I/O操作,。設(shè)備類型指定了驅(qū)動程序管理的設(shè)備的類型,如存儲設(shè)備,。

全局驅(qū)動數(shù)據(jù)

當(dāng)一個設(shè)備對象被創(chuàng)建時,,可以將一個內(nèi)存塊與之關(guān)聯(lián),,該內(nèi)存塊叫DeviceExtension,也即在Windows中驅(qū)動程序數(shù)據(jù)保存的地方,。這是一個挺重要的東東,,它使得在驅(qū)動程序代碼中使用難于維護(hù)的全局?jǐn)?shù)據(jù)結(jié)構(gòu)變得沒有必要。例如,,要是錯誤的聲明了一個和全局變量具有相同名稱的局部變量,,驅(qū)動程序編寫者會發(fā)現(xiàn)難以跟蹤這樣的bug。這也使得維護(hù)特定于設(shè)備對象的數(shù)據(jù)變得簡單,,尤其是當(dāng)多個設(shè)備對象存在于一個驅(qū)動程序中的時候,,比如總線驅(qū)動程序在管理總線上出現(xiàn)的多個設(shè)備的物理設(shè)備對象時。

設(shè)備命名

設(shè)備的名稱可在設(shè)備對象被創(chuàng)建的時候賦予,,這個名稱可以用來訪問驅(qū)動的句柄,,句柄又被用來進(jìn)行I/O操作。Microsoft建議不要給在過濾驅(qū)動和功能驅(qū)動中創(chuàng)建的功能設(shè)備對象命名,。Oney [Oney, 99]指出,,若一個設(shè)備對象具有名稱,則任意用戶都能打開設(shè)備對象并對其進(jìn)行I/O操作,,即使是對非磁盤設(shè)備驅(qū)動,。這是因為Windows默認(rèn)就給了非磁盤設(shè)備對象毫無限制的訪問狀態(tài)。另外一個問題是這些名稱不需要遵循任何命名規(guī)范,,指定的名稱往往不是經(jīng)過挑選的,。例如兩個驅(qū)動程序開發(fā)者可能給他們的設(shè)備對象賦予相同的名稱,這樣就會引起沖突,。Windows還支持另外一種設(shè)備對象命名方式,,即設(shè)備接口。設(shè)備接口是由128比特位構(gòu)成的全局唯一標(biāo)識符[Open Group, 97],。GUID可用Microsoft DDK中提供的工具生成,,生成之后可對外發(fā)布。驅(qū)動程序通過在AddDevice例程中調(diào)用I/O管理器的名為IoRegisterDeviceInterface的例程注冊設(shè)備接口,。一旦注冊,驅(qū)動程序必須調(diào)用I/O管理器的IoSetDeviceInterfaceState例程來使能設(shè)備接口,。注冊過程中一個接口數(shù)據(jù)入口被添加到Windows注冊表中,,應(yīng)用程序接著就可以訪問到。

從應(yīng)用程序訪問驅(qū)動

應(yīng)用程序欲對設(shè)備驅(qū)動執(zhí)行I/O操作前,,必須先通過調(diào)Win32 API CreateFile獲取到設(shè)備驅(qū)動的一個句柄,,這個API需要設(shè)備的路徑作為參數(shù),如\device\devicex,。具有名稱的設(shè)備將出現(xiàn)在命名空間“\\device”中,,因此先前的路徑表示設(shè)備devicex,。CreateFile同時需要指定對設(shè)備的訪問標(biāo)志,如讀,、寫和共享方式,。對注冊了設(shè)備接口而沒有名稱的設(shè)備的訪問則不同于圖3.1.2.4展示的例程,它需要使用驅(qū)動程序的GUID,,調(diào)用Win32 API SetupDiGetClassDevs獲取一個指向設(shè)備信息結(jié)構(gòu)的句柄,。這種方式只適用于驅(qū)動程序已經(jīng)注冊了設(shè)備接口、應(yīng)用程序需要訪問設(shè)備(叫設(shè)備接口類)的情況,。每次驅(qū)動程序調(diào)用I/O管理器例程IoRegisterDeviceInterface時,,一個新的設(shè)備接口類的實例就被創(chuàng)建。一旦應(yīng)用程序獲取到了設(shè)備信息句柄,,對Win32 API SetupDiEnumDeviceInterfaces的多個調(diào)用將會為每個設(shè)備接口類實例返回設(shè)備接口數(shù)據(jù),。最后,通過調(diào)用Win32 API SetupGetDeviceInterfaceDetail,,并根據(jù)之前返回的接口數(shù)據(jù),,可為每個設(shè)備接口類實例獲取到一個設(shè)備路徑。接著,,對感興趣的設(shè)備,,使用設(shè)備路徑為參數(shù)調(diào)用CreateFile來獲取句柄以便執(zhí)行I/O操作。

 

Figure 3.1.2.4 Obtaining a handle an application can use for I/O from a device GUID.

設(shè)備對象棧

當(dāng)PnP管理器調(diào)用AddDevice例程時,,它其中的一個參數(shù)是來自下層驅(qū)動的一個設(shè)備對象(PDO),。設(shè)備對象在AddDevice例程中完成堆疊,因為發(fā)往下層驅(qū)動的IRP可被當(dāng)前加載的驅(qū)動獲取到,。如圖 3.1.2.5所示,,設(shè)備堆疊是通過調(diào)用I/O管理器例程IoAttachDeviceToDeviceStack 來完成。在調(diào)用IoAttachDeviceToDeviceStack時,,需要一個位于棧中新創(chuàng)建設(shè)備對象下方的物理設(shè)備對象,。這個例程將新創(chuàng)建的設(shè)備附加到設(shè)備棧的頂層,并將當(dāng)前位于其下方的設(shè)備對象返回,,圖 3.1.2.5中下方設(shè)備為設(shè)備對象X,。下層的物理設(shè)備可位于新設(shè)備下方的任何位置,而IoAttachDeviceToStack 返回的是緊鄰著當(dāng)前設(shè)備的下層設(shè)備,。

 

Figure 3.1.2.5 Attaching a device object to the top of a device object stack.

Windows應(yīng)用層到內(nèi)核和內(nèi)核到應(yīng)用層數(shù)據(jù)傳輸模式

從內(nèi)核空間到用戶空間以及從用戶空間到內(nèi)核空間傳送數(shù)據(jù)的模式是在設(shè)備對象的flag域中設(shè)置,。共有三種模式,分別為buffer I/O,,direct I/Oneither I/O,。圖3.1.2.6 闡述了這三種模式。在buffer I/O模式中,操作系統(tǒng)分配了一個內(nèi)核緩沖區(qū)來處理請求,。在寫操作中,,操作系統(tǒng)首先驗證用戶空間提供的緩沖區(qū),然后從用戶空間將數(shù)據(jù)拷貝到新分配的內(nèi)核緩沖區(qū),,接著講內(nèi)核緩沖區(qū)傳送給驅(qū)動程序,。讀操作時,操作系統(tǒng)驗證用戶緩沖區(qū)然后將數(shù)據(jù)從新分配的內(nèi)核緩沖區(qū)中拷貝到用戶緩沖區(qū),。驅(qū)動程序可通過IRPAssociatedIrp.SystemBuffer域訪問到內(nèi)核緩沖區(qū),。當(dāng)使用buffer I/O模式時,驅(qū)動程序通過讀取或?qū)懭雰?nèi)核緩沖區(qū)來實現(xiàn)與應(yīng)用層的通信,。

Direct I/O是用于應(yīng)用層和驅(qū)動程序交換數(shù)據(jù)的第二種模式,。應(yīng)用層提供的緩沖區(qū)被操作系統(tǒng)在內(nèi)存中鎖定,這樣它就不會被交換出去,,并將被鎖定內(nèi)存的內(nèi)存描述列表(Memory Description List,,MDL)傳送給驅(qū)動程序。內(nèi)存描述列表是一個不透明的結(jié)構(gòu)體,,它的實現(xiàn)對驅(qū)動程序是不可見的,。驅(qū)動程序之后通過MDL對用戶空間緩沖區(qū)進(jìn)行DMA操作。驅(qū)動程序通過IRPMdlAddress域訪問MDL,。使用direct I/O的好處是它比buffer I/O速度要快,,因為不需要在用戶層和內(nèi)核層之間拷貝任何數(shù)據(jù),而是直接對用戶緩沖區(qū)進(jìn)行I/O操作,。

第三種I/O模式既不使用buffer也不使用MDLs,,操作系統(tǒng)直接將用戶空間緩沖區(qū)的虛擬地址傳送給驅(qū)動程序。驅(qū)動程序在使用之前負(fù)責(zé)檢查緩沖區(qū)的有效性,。此外,,只有在當(dāng)前線程上下文環(huán)境和應(yīng)用程序的上下文環(huán)境一致時,用戶空間緩沖區(qū)才能被訪問,,否則會出現(xiàn)頁錯誤,,因為虛擬地址只有在應(yīng)用程序所對應(yīng)的進(jìn)程處于激活狀態(tài)時才有效。

 

 

Figure 3.1.2.6 The three ways in which data from kernel to user and user to kernel

space is exchanged.

分發(fā)例程

分發(fā)例程用來處理接收到的I/O請求包,,即IRPs(I/O request packets),。當(dāng)一個IRP到來(如當(dāng)一個應(yīng)用程序發(fā)起I/O操作)時,一個適當(dāng)?shù)睦瘫粡尿?qū)動對象的MajorFunction域中指定的例程數(shù)組中選出來,,如圖3.1.3,。這些例程在驅(qū)動程序的入口函數(shù)中被初始化。每個IRP在創(chuàng)建時就與一個I/O stack location結(jié)構(gòu)體(用于存儲IRP的參數(shù))關(guān)聯(lián),。這個結(jié)構(gòu)體有一個域,指定了IRP需要執(zhí)行的分發(fā)例程和分發(fā)例程需要的相關(guān)參數(shù),。I/O管理器根據(jù)IRP決定將IRP發(fā)往哪個分發(fā)例程,。

 

Figure 3.1.3 dispatching IRP’s to dispatch routines.

 

因此,,IRPs被路由到適當(dāng)?shù)尿?qū)動例程進(jìn)而得到處理。分發(fā)例程ID如表3.1.3所示,,它們作為例程數(shù)組的索引,,這個數(shù)組是在驅(qū)動對象的MajorFunction域中指定。分發(fā)例程的名稱是驅(qū)動程序所實現(xiàn)的例程的名稱,,這些例程都將一個IRP和該IRP被發(fā)送到的設(shè)備對象作為參數(shù),。

 

Table 3.1.3 Required Windows driver dispatch routines

Windows驅(qū)動程序安裝

Windows根據(jù)一個INF文件中的安裝信息來安裝驅(qū)動。驅(qū)動程序的編寫者負(fù)責(zé)為驅(qū)動程序提供一個INF文件,。Windows DDK提供一個叫GenInfGUI應(yīng)用程序,,來為驅(qū)動程序生成INF文件。這個工具需要提供一個公司名稱和一個Windows設(shè)備類,,驅(qū)動程序?qū)⒈话惭b到該設(shè)備類下,。Windows為驅(qū)動程序預(yù)定義了各種不同的設(shè)備類。從系統(tǒng)控制面板進(jìn)入到設(shè)備管理器面板,,可看到顯示的所有按設(shè)備類分類的已安裝驅(qū)動程序,。已有的設(shè)備類如1394PCMCIA設(shè)備類??稍?/span>INF文件中添加一個ClassInstall32節(jié)來添加一個自定義的設(shè)備類,。對PnP感知的設(shè)備,還需要在INF文件中指定一個硬件ID,,在該設(shè)備被添加到系統(tǒng)中時,,系統(tǒng)將用該ID來標(biāo)識設(shè)備。硬件ID是一個標(biāo)識字符串,,PnP管理器在設(shè)備添加到系統(tǒng)中用硬件ID來標(biāo)識設(shè)備,。MicrosoftWindows系統(tǒng)會用到的各種設(shè)備發(fā)布了硬件ID。硬件ID保存在硬件設(shè)備中,,操作系統(tǒng)在設(shè)備添加到系統(tǒng)中時從設(shè)備讀取,。一旦新設(shè)備的INF文件成功安裝到系統(tǒng)中,每當(dāng)具有指定硬件ID的設(shè)備被添加到系統(tǒng)中,,為該設(shè)備編寫的驅(qū)動程序都被加載,,并在設(shè)備移除時被卸載。

Windows獲取驅(qū)動程序使用信息

系統(tǒng)控制面板上的設(shè)備管理器給用戶提供驅(qū)動的相關(guān)信息,。它列出了所有當(dāng)前已加載的驅(qū)動,,每個驅(qū)動提供者的相關(guān)信息和驅(qū)動資源使用情況。同時還顯示驅(qū)動無法加載時的失敗信息以及錯誤碼,。

Linux驅(qū)動架構(gòu)組件

Linux下的設(shè)備驅(qū)動和Windows設(shè)備驅(qū)動的相似之處在于它們都是由一些執(zhí)行I/O及控制操作的例程組成,。驅(qū)動程序沒有對應(yīng)的驅(qū)動對象,而是由內(nèi)核直接管理。

驅(qū)動程序初始化

Linux下的美國各驅(qū)動程序包含一個驅(qū)動注冊例程和反注冊例程,。驅(qū)動注冊例程類似于Windows的驅(qū)動入口例程,。驅(qū)動程序編寫者使用內(nèi)核定義的兩個宏module_initmodule_exit來指定自定義的例程作為注冊和反注冊例程。

 

3.2.1.1. 驅(qū)動注冊和反注冊

module_init聲明的注冊例程是驅(qū)動程序被加載時第一個執(zhí)行的例程,。在這個例程中,,用一個內(nèi)核字符設(shè)備注冊例程register_chrdev注冊驅(qū)動。這個例程需要一個驅(qū)動名稱,、主驅(qū)動編號(將在3.2.2節(jié)中討論)和一系列執(zhí)行文件操作的例程,。其它特定于驅(qū)動的初始化也必須在這個例程中完成。反注冊函數(shù)在驅(qū)動程序被卸載時執(zhí)行,,它的主要功能是做一些清除操作,。反注冊之前使用register_chrdev注冊的驅(qū)動程旭時會調(diào)用內(nèi)核例程unregister_chrdev,并需以設(shè)備名和主編號為參數(shù),。

設(shè)備命名

Linux下,,設(shè)備命名使用0255的數(shù)字,叫主設(shè)備編號,。這意味著最多只能有256個可用的設(shè)備,,也即應(yīng)用程序可獲取到句柄的設(shè)備。但這樣一個主設(shè)備的每個驅(qū)動程序可以管理額外的256個設(shè)備,。這些驅(qū)動程序管理的設(shè)備也使用0255的數(shù)字標(biāo)識,,叫次設(shè)備編號。因此,,應(yīng)用程序可訪問多達(dá)65535(256*256)個設(shè)備,。主設(shè)備編號賦給一些熟知的設(shè)備,如IEEE1394的編號為171,。Linux內(nèi)核源碼樹中的文件Documentation/devices.txt包含了所有主設(shè)備編號的分配情況和編號注冊中心的聯(lián)系地址,。當(dāng)前,主設(shè)備編號240-254為實驗所用,。一個驅(qū)動程序通過指定0作為主設(shè)備編號來請求一個自動分配的主編號(若當(dāng)前還有可用的主設(shè)備編號的話),。這種指定0為主設(shè)備編號的方式并不會有什么問題,因為它是為null設(shè)備預(yù)留的,,而沒有一個新的驅(qū)動程序會將自己注冊為null設(shè)備驅(qū)動,。

應(yīng)用程序訪問驅(qū)動

應(yīng)用程序通過文件系統(tǒng)入口(nodes)訪問驅(qū)動。按照慣例,,驅(qū)動程序目錄為/dev,。需要對驅(qū)動執(zhí)行I/O操作的應(yīng)用程序使用open系統(tǒng)調(diào)用來獲取某個特定驅(qū)動的句柄。Open系統(tǒng)調(diào)用需要一個設(shè)備節(jié)點名稱如/dev/tty和訪問標(biāo)識(flags),。獲取句柄之后,,應(yīng)用程序使用該句柄來調(diào)用其他的I/O系統(tǒng)調(diào)用如read,、writeIOCTL

文件操作

Windows下,,分發(fā)例程是在驅(qū)動入口例程中設(shè)置,。Linux下,這些分發(fā)例程就是所謂的文件操作并使用結(jié)構(gòu)體file_operations來表示,。一個典型的驅(qū)動程序會實現(xiàn)如表3.2.3列出的文件操作例程。

 

Table 3.2.3 Most commonly defined driver file operations in Linux

 

這些文件操作在驅(qū)動程序注冊時指定,。每當(dāng)應(yīng)用程序請求一個設(shè)備句柄時,,內(nèi)核會創(chuàng)建一個叫file的結(jié)構(gòu)體,并在某個驅(qū)動例程被調(diào)用時將其傳遞給驅(qū)動程序,。文件操作例程被多個用戶調(diào)用,,每個都對應(yīng)一個file結(jié)構(gòu)體。File結(jié)構(gòu)體有一個f_op域,,這個域是一個指針,,指向驅(qū)動注冊時指定的文件操作例程集。因此,,在調(diào)用任何一個文件操作例程時,,都可以通過改變f_op域的值來指向新的文件操作例程集。

驅(qū)動程序全局?jǐn)?shù)據(jù) 

每當(dāng)應(yīng)用程序?qū)?span>/dev下的設(shè)備文件節(jié)點發(fā)起一個open系統(tǒng)調(diào)用時,,應(yīng)用程序從操作系統(tǒng)獲得設(shè)備的一個句柄,。這個時候驅(qū)動程序的open函數(shù)被調(diào)用,并給它傳遞為open系統(tǒng)調(diào)用所創(chuàng)建的file結(jié)構(gòu)體,。任何一個文件操作例程執(zhí)行時,,內(nèi)核都將file結(jié)構(gòu)體傳遞給驅(qū)動程序。File結(jié)構(gòu)體的private_data域可以是驅(qū)動程序指定的任意自定義結(jié)構(gòu)體,。驅(qū)動程序的私有數(shù)據(jù)通常在文件open操作函數(shù)中被設(shè)置,,即為它分配內(nèi)存,之后在文件的release操作函數(shù)中釋放該內(nèi)存,。File結(jié)構(gòu)體的私有數(shù)據(jù)域可用來指向驅(qū)動程序的全局?jǐn)?shù)據(jù),,避免了使用全局變量。

驅(qū)動主編號和次編號如何工作

3.2.4.1. 問題

Linux下只有一個驅(qū)動程序可通過注冊一個特定的主編號來管理一個設(shè)備,,也就是說,,驅(qū)動程序的注冊只能使用一個主編號。舉個例子,,存在兩個設(shè)備節(jié)點/dev/device1(主編號4次編號1)和設(shè)備/dev/device2(主編號4次編號2),,只有一個驅(qū)動程序能夠處理應(yīng)用程序?qū)蓚€節(jié)點的請求,。這種限制的存在是因為Linux沒有提供一種注冊機制使得驅(qū)動程序能自注冊一個主編號和一個次編號以便能管理一個設(shè)備,。

 

3.2.4.2.解決辦法

· 加載一個驅(qū)動程序來管理主編號為4的設(shè)備。這個驅(qū)動程序?qū)⒆约涸趦?nèi)核中注冊(節(jié)3.2.1.1會看到這是如何完成的),。

· 分別加載兩個驅(qū)動,一個管理主編號4次編號1的設(shè)備,,另一個管理主編號4次編號2的設(shè)備,。這兩個驅(qū)動程序沒有在內(nèi)核中注冊,而是向管理主設(shè)備編號4的另外一個驅(qū)動程序注冊,。這個驅(qū)動程序負(fù)責(zé)實現(xiàn)注冊機制并跟蹤管理向它注冊的所有驅(qū)動程序,。

 

· 應(yīng)用程序打開任意一個設(shè)備節(jié)點(/dev/device1/dev/device2)時,注冊為管理主設(shè)備4的驅(qū)動程序的open例程將被內(nèi)核調(diào)用,。一個用來表示被打開設(shè)備的file結(jié)構(gòu)體作為參數(shù)傳遞給這個open例程,。

· 這時候,管理設(shè)備主編號4的驅(qū)動程序修改文件操作函數(shù)指針(file結(jié)構(gòu)體的f_op成員)來指向管理被打開設(shè)備的驅(qū)動程序所實現(xiàn)的I/O例程,。應(yīng)用程序打開次設(shè)備時,,管理主編號4的驅(qū)動程序以下列方式區(qū)分:

o 一個叫inode的結(jié)構(gòu)體被傳遞給open例程。這個結(jié)構(gòu)體包含一個叫i_rdev的域,,該域指定了open操作的目標(biāo)設(shè)備對應(yīng)的主編號和次編號,。內(nèi)核的MINORMAJOR宏可用來從i_rdev域提取住次編號。這個例子中,,主編號為4,,次編號為12。管理主編號4的驅(qū)動程序就可以通過這個信息從它的注冊數(shù)據(jù)庫中定位到次設(shè)備驅(qū)動程序,。

用戶到內(nèi)核和內(nèi)核到用戶空間數(shù)據(jù)傳輸模式

Linux下,,用戶到內(nèi)核和內(nèi)核到用戶空間的數(shù)據(jù)交換方式有三種,分別是buffer I/O,,direct I/Ommap,。在buffer I/O模式中,內(nèi)核將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間供驅(qū)動程序使用,。和Windows不同,,Linux沒有自動對I/O進(jìn)行緩沖,而是提供訪問用戶和內(nèi)核空間的例程,,驅(qū)動程序使用這些例程來完成用戶和內(nèi)核空間之間數(shù)據(jù)的拷貝,。Direct I/O模式中,驅(qū)動程序可對用戶空間緩沖區(qū)直接讀和寫,。這是通過kiobuf接口完成,,它將用戶空間緩沖區(qū)映射到調(diào)用系統(tǒng)調(diào)用kiobuf時定義的結(jié)構(gòu)體。這個操作會鎖定用戶空間緩沖區(qū),,這樣該空間不會被換出以便滿足設(shè)備的I/O操作,。第三種方式是mmap,它是由驅(qū)動程序使用mmap內(nèi)核調(diào)用將內(nèi)核的空間塊映射到用戶空間,,應(yīng)用程序因而可以對映射的內(nèi)核內(nèi)存進(jìn)行I/O操作[Rubini et al, 01].,。

Linux驅(qū)動安裝

Linux下驅(qū)動程序的安裝是將驅(qū)動文件放置到特定的系統(tǒng)目錄下,。在RedHat發(fā)行版中[Redhat, 02],模塊位于目錄/lib/modules/kernel_version,,kernel_version指定當(dāng)前內(nèi)核版本,,如2.4.19。一個叫modules.conf的配置文件位于系統(tǒng)配置文件目錄如/etc中,,這個文件在加載模塊時被內(nèi)核使用,。通過修改該文件,可對某個驅(qū)動程序放置位置進(jìn)行覆蓋,。還可以定義其它一些模塊加載選項,,如驅(qū)動被加載時給它傳遞的參數(shù)。模塊加載和卸載使用系統(tǒng)自帶的內(nèi)核模塊工具包,,叫insmodmodprobermmod,。Insmodmodprobe將驅(qū)動程序二進(jìn)制鏡像加載到內(nèi)核,,rmmod則移除模塊。另一個叫lsmod的程序列出當(dāng)前所有已加載的模塊,。Insmod嘗試加載一個模塊,,若該模塊依賴于其他模塊,則返回一個錯誤碼,。Modprobe則嘗試著滿足模塊依賴關(guān)系,,它試圖將當(dāng)前模塊所依賴的其它模塊先進(jìn)行加載。模塊依賴關(guān)系信息可從一個叫modules.dep的文件獲取,,該文件位于系統(tǒng)模塊目錄(system’s modules directory)中,。在驅(qū)動程序可被應(yīng)用程序訪問前,這個驅(qū)動的一個附有主次設(shè)備編號的設(shè)備節(jié)點(2.13.2.2.1節(jié),,Linux如何在系統(tǒng)中表示設(shè)備)首先要在設(shè)備目錄/dev中被創(chuàng)建,。系統(tǒng)程序mknod就是為這個目的準(zhǔn)備的。在創(chuàng)建一個設(shè)備節(jié)點時,,指定節(jié)點為字符設(shè)備還是塊設(shè)備是有必要的,。

Linux獲取驅(qū)動使用信息

我們時常需要獲取系統(tǒng)已加載驅(qū)動的狀態(tài)信息。Linux下,,proc文件系統(tǒng)是用來將內(nèi)核信息向應(yīng)用程序發(fā)布,。Proc文件系統(tǒng)和其他的文件一樣,它也包含目錄和文件節(jié)點供應(yīng)用程序訪問和執(zhí)行I/O操作,。Proc文件系統(tǒng)中的文件和普通文件的區(qū)別在于,,對proc文件執(zhí)行I/O操作的數(shù)據(jù)是被傳遞到內(nèi)核內(nèi)存而不是磁盤存儲。Proc文件系統(tǒng)是應(yīng)用程序和內(nèi)核組件之間的通信媒介,。例如,,讀取/proc/modules將返回當(dāng)前所有已加載模塊和它們的依賴關(guān)系,。在獲取驅(qū)動狀態(tài)信息和發(fā)布驅(qū)動程序數(shù)據(jù)到應(yīng)用程序時,proc文件系統(tǒng)就尤為有用,。

WindowsLinux驅(qū)動架構(gòu)組件比較

WindowsLinux的驅(qū)動程序都是由一系列執(zhí)行I/O操作的例程組成的可動態(tài)加載的模塊,。當(dāng)加載一個模塊時,內(nèi)核將定位到被系統(tǒng)標(biāo)記為驅(qū)動程序入口的例程作為驅(qū)動代碼執(zhí)行的起點,。

驅(qū)動例程

兩個系統(tǒng)中驅(qū)動程序都具有初始化和反初始化例程,。在Linux中,這兩個例程的名稱可自定義,,在Windows中,,初始化例程的名稱固定(DriverEntry)但反初始化例程可自定義。Windows為每個驅(qū)動程序維護(hù)一個驅(qū)動對象,,驅(qū)動程序的多個實例用多個驅(qū)動對象表示,。Linux下,內(nèi)核為每個管理一個設(shè)備主編號的驅(qū)動維護(hù)信息,,即每個主設(shè)備驅(qū)動,。兩個操作系統(tǒng)都要求驅(qū)動程序?qū)崿F(xiàn)標(biāo)準(zhǔn)的I/O例程,Windows中叫分發(fā)例程,,Linux下叫文件操作,。Linux下,可為每個應(yīng)用程序獲取到的設(shè)備句柄設(shè)置一個不同的文件操作例程集,。Windows下,,分發(fā)例程在驅(qū)動對象的一部分,并且是一次性地在DriverEntry例程中定義,。由于每個被加載的驅(qū)動都有一個驅(qū)動對象,,因此不建議在應(yīng)用程序使用系統(tǒng)調(diào)用請求一個句柄時修改驅(qū)動對象的分發(fā)例程。Windows有個叫AddDevice的例程,,在PnP感知的設(shè)備添加到系統(tǒng)時被PnP管理器調(diào)用,。Linux沒有PnP管理器,也就不存在這樣一個例程,。

Windows的分發(fā)例程對設(shè)備對象和IRPs進(jìn)行操作,,Linux下,文件操作針對file結(jié)構(gòu)體,。自定義的驅(qū)動全局?jǐn)?shù)據(jù)保存在Windows的設(shè)備對象中,,而Linux下則保存在file結(jié)構(gòu)體中。Windows下,,設(shè)備對象在驅(qū)動加載時被創(chuàng)建,,Linux下,file結(jié)構(gòu)體是應(yīng)用程序通過系統(tǒng)調(diào)用open向驅(qū)動請求句柄時被創(chuàng)建,。這就意味著Linux下每個應(yīng)用程序的全局?jǐn)?shù)據(jù)可保存在file操作結(jié)構(gòu)體中,。Windows下,,全局?jǐn)?shù)據(jù)只能出現(xiàn)在驅(qū)動管理的功能設(shè)備對象(FDO)中。Windows下每個應(yīng)用程序的全局?jǐn)?shù)據(jù)必須保存在功能設(shè)備對象(FDO)自定義結(jié)構(gòu)體的列表結(jié)構(gòu)中,。

設(shè)備命名

Windows下的驅(qū)動使用驅(qū)動自定義的字符串命名并顯示在\\device命名空間下,。Linux下,驅(qū)動被賦予文本形式的名稱,,但應(yīng)用程序并不需要知道這些名稱,,驅(qū)動是通過主-次編號對來標(biāo)識。主-次編號的范圍是0-255,,因為是用16比特位來表示主-次編號對,,所以最大允許65535個設(shè)備安裝到系統(tǒng)中。Linux下的設(shè)備通過文件系統(tǒng)節(jié)點供應(yīng)用程序訪問,。在大部分的Linux發(fā)行版中,,目錄/dev包含設(shè)備文件系統(tǒng)節(jié)點。每個節(jié)點創(chuàng)建時帶有驅(qū)動的主編號和次編號,。應(yīng)用程序獲得驅(qū)動的一個句柄,,用來對系統(tǒng)調(diào)用open的目標(biāo)設(shè)備節(jié)點進(jìn)行I/O操作。Windows還有另一種驅(qū)動命名方式,,是給每個驅(qū)動注冊的128GUID。應(yīng)用程序訪問注冊表,,通過GUID獲得\\device命名空間下的文本形式的名稱,。這個名稱通過使用Win32 API CreateFile來獲取驅(qū)動的一個句柄以便進(jìn)行I/O操作。

 

用戶-內(nèi)核空間數(shù)據(jù)交換

兩種操作系統(tǒng)中,,數(shù)據(jù)來自或去往用戶空間的方式是類似的,,都允許緩沖區(qū)數(shù)據(jù)傳送,在Windows下是有I/O管理器執(zhí)行,,Linux下則由驅(qū)動執(zhí)行,。兩種操作系統(tǒng)都可以進(jìn)行direct I/O到用戶空間緩沖區(qū),通過鎖定用戶空間緩沖區(qū)以使得該緩沖區(qū)一直存在于物理內(nèi)存中,。這個起因是驅(qū)動程序并不總能直接訪問用戶空間緩沖區(qū),,因為它不能保證一直運行在和擁有該用戶空間緩沖區(qū)的應(yīng)用程序一致的進(jìn)程上下文中。應(yīng)用程序有它自己的虛擬地址空間,,該地址空間只在它自己的進(jìn)程上下文中有效,。因此,當(dāng)驅(qū)動程序訪問某些應(yīng)用程序的一個虛擬地址但不在該應(yīng)用程序的進(jìn)程上下文中時,,就會訪問了無效的地址,。

 

驅(qū)動安裝和管理

Windows驅(qū)動的安裝是通過一個叫INF文件的文本文件。一旦安裝之后,,一個設(shè)備的驅(qū)動程序在設(shè)備出現(xiàn)在系統(tǒng)中時會自動被PnP管理器加載,。Linux系統(tǒng)中,,使用程序工具來加載驅(qū)動二進(jìn)制鏡像到內(nèi)核。需要手動將一些條目添加到系統(tǒng)啟動文件中,,這樣驅(qū)動加載程序如modprobe就以驅(qū)動程序鏡像路徑或驅(qū)動程序的別名為參數(shù)執(zhí)行,。驅(qū)動程序的別名在文件/etc/modules.conf中定義,modprobe等類似程序在加載驅(qū)動之前會查看該文件,。Modules.conf中一個定義別名的條目的例子可類似于“alias sounddriver testdriver”,,這是將sounddriver作為testdriver驅(qū)動二進(jìn)制鏡像的別名。這樣一來,,用戶可通過使用標(biāo)準(zhǔn)的更簡單的名稱如sounddriver來加載音頻驅(qū)動程序而不需要知道音頻卡的某個特定驅(qū)動程序的名稱,。Windows下驅(qū)動程序的狀態(tài)信息可在設(shè)備管理面板中看到,也可以直接從系統(tǒng)注冊表中讀取相關(guān)數(shù)據(jù),。Linux下,,驅(qū)動信息可通過proc文件系統(tǒng)節(jié)點獲取,如文件/proc/module包含了已加載模塊的一個列表,。

 

一個內(nèi)核緩沖驅(qū)動

這一節(jié)展示一個執(zhí)行I/O操作到內(nèi)核內(nèi)存塊(虛擬磁盤)的簡單驅(qū)動程序的實現(xiàn),。我們將討論為使驅(qū)動程序能夠同時在WindowsLinux下工作所需要的各種組件,這樣它們所需要驅(qū)動組件的相似和不同之處也得到了突顯,。驅(qū)動程序所管理的虛擬設(shè)備如圖4.0.所示,,它由若干內(nèi)核內(nèi)存塊組成。應(yīng)用程序可對虛擬設(shè)備進(jìn)行I/O操作,。驅(qū)動程序可以選擇某個內(nèi)存塊和內(nèi)存塊的偏移位置進(jìn)行訪問,。

 

Figure 4.0 A simple virtual device

 

需要的驅(qū)動組件

WindowsLinux驅(qū)動程序都將實現(xiàn)readwriteIOCTL驅(qū)動例程,。每個操作系統(tǒng)所需要的例程如圖4.1.所示,。驅(qū)動程序的名稱可隨意指定。圖4.1種不同操作系統(tǒng)的例程也可以被賦以相同的名稱,,這里只是根據(jù)平臺的慣用法來命名,。

 

Figure 4.1 The Windows and Linux basic driver routines

 

驅(qū)動加載和卸載例程

Windows下,驅(qū)動加載例程DriverEntry中所執(zhí)行的步驟是設(shè)置I/O分發(fā)例程,,如圖4.1.1a所示,。

 

Figure 4.1.1a Initialisation of a driver Object in the driver entry routine

 

Linux下,驅(qū)動加載例程RegisterDriver中所進(jìn)行的是驅(qū)動主編號的注冊,,如圖4.1.1b,。Tagged文件操作的初始化,只針對GCC編譯器,,如圖4.1.1b,,是在對結(jié)構(gòu)體fops的聲明中,當(dāng)然這不是ANSI C的有效語法。編譯器將使用驅(qū)動程序?qū)崿F(xiàn)的例程名稱初始化file_operation結(jié)構(gòu)體(fops)中的各個不同的域,。如open是結(jié)構(gòu)體一個域的名稱而Open是驅(qū)動實現(xiàn)的一個例程,,編譯器將Open函數(shù)指針賦給open域。


Figure 4.1.1b Registration of a driver major number in Linux

 

Linux驅(qū)動的卸載程序中,,已注冊驅(qū)動必須進(jìn)行反注冊,,如圖4.1.1c

 

Figure 4.1.1c Driver major number deregistration in Linux

 

驅(qū)動全局結(jié)構(gòu)

必須定義一個結(jié)構(gòu)體來保存驅(qū)動全局?jǐn)?shù)據(jù),,這些數(shù)據(jù)在驅(qū)動的各例程中被使用,。對這個內(nèi)存設(shè)備,同樣的結(jié)構(gòu)使用在WindowsLinux驅(qū)動程序中,,其定義如圖4.1.2,。


Figure 4.1.2 Structure used to store global data for generic driver

 

memoryBank是包含4個內(nèi)存塊,每個塊為1K大小的數(shù)組,。currentBank表示當(dāng)前選中的內(nèi)存塊,,offsets記錄了每個內(nèi)存塊內(nèi)部的偏移量。

 

添加設(shè)備例程

 

添加設(shè)備例程只針對Windows,。Linux沒有添加設(shè)備例程,,所有的初始化必須在驅(qū)動加載例程里完成。WindowsaddDevice例程所執(zhí)行的操作如圖4.1.3所示,。在調(diào)用I/O管理器例程IoCreateDevice例程時,,一個設(shè)備對象被創(chuàng)建。供應(yīng)用程序使用來獲取驅(qū)動句柄的接口也被創(chuàng)建,,這通過調(diào)用I/O管理器例程IoRegisterDeviceInterface,。這個例程的一個參數(shù)是使用系統(tǒng)工具guidgen手動生成的GUIDWindows下,,驅(qū)動和應(yīng)用程序之間的不同數(shù)據(jù)交換方式在3.1.2.6節(jié)中有說明,。驅(qū)動程序通過設(shè)置設(shè)備對象的flags(3.13.2關(guān)于設(shè)備對象的討論)來表明其要使用的數(shù)據(jù)交換方法,。這里例子中flags被設(shè)置成使驅(qū)動使用buffered I/O方式,。內(nèi)存設(shè)備使用的每個內(nèi)存塊通過其中一個叫ExAllocatePool的內(nèi)核內(nèi)存分配例程來分配。這個內(nèi)存從內(nèi)核的非頁內(nèi)存池中分配,,這樣設(shè)備的內(nèi)存總是存在于物理內(nèi)存中,。

 

Figure 4.1.3 Operations performed in the Windows driver’s add device routine

 

打開和關(guān)閉例程

 

Windows驅(qū)動的大部分初始化操作都已經(jīng)在添加設(shè)備例程中完成,因此不需要在打開例程中做任何初始化,。Linux下的打開例程如圖4.1.4a所示,。首先,用于保存驅(qū)動全局?jǐn)?shù)據(jù)的內(nèi)存被分配,,然后將文件結(jié)構(gòu)體的private_data域指向該內(nèi)存,。之后內(nèi)存設(shè)備所使用的內(nèi)存塊通過和Windows下完全一樣的方式分配,只是內(nèi)存分配函數(shù)的名字不同,Windows下是ExAllocatePoolLinux下是kmalloc,。

 

Figure 4.1.4a Operations performed in Linux’s generic driver open routine

 

Linux的關(guān)閉例程中,,為驅(qū)動全局?jǐn)?shù)據(jù)和內(nèi)存設(shè)備分配的內(nèi)存被釋放,如圖4.1.4b,。Windows下,,內(nèi)存的釋放是在響應(yīng)PnP移除消息的時候,這個在本節(jié)后面會有討論,。

 

Figure 4.1.4b Operations performed in Linux’s generic driver close routine

 

讀和寫例程

Readwrite例程將數(shù)據(jù)傳送到或取自當(dāng)前選中的內(nèi)核內(nèi)存塊,。Windows下,讀例程的執(zhí)行如圖4.1.5a所示,。要讀取數(shù)據(jù)的長度值從IRPI/O棧位置(3.1.3節(jié)什么是I/O stack location)獲取,,該域名稱為Parameters.Read.Length。所請求長度的數(shù)據(jù)將被從當(dāng)前選中的內(nèi)存塊(后面會討論應(yīng)用程序通過驅(qū)動IOCTL例程選擇內(nèi)存塊)中讀取,,使用的是內(nèi)核運行時例程RtlMoveMemory,。RtlMoveMemory將數(shù)據(jù)從內(nèi)存設(shè)備的內(nèi)存空間搬移到I/O管理器為buffered I/O分配的緩沖區(qū),也就是IRPAssociatedIrp.SystemBuffer域,。這個IRP算完成了,,就通知I/O管理器驅(qū)動程序已完成IRP的處理,I/O管理器將IRP返回給其發(fā)起者,。

 

Figure 4.1.5a Performing a read operation in the Windows driver

寫例程對上述內(nèi)存搬移進(jìn)行反操作,,如圖4.1.5b

 

Figure 4.1.5b Performing a write operation in the Windows driver

 

Linux下,,讀例程如圖4.1.5c所示,。對驅(qū)動全局?jǐn)?shù)據(jù)的引用從文件結(jié)構(gòu)體的private_data域獲取,從全局?jǐn)?shù)據(jù)中,,又獲取到對memoryBank的引用,。接著數(shù)據(jù)就從這個內(nèi)存區(qū)被傳送到用戶空間,使用內(nèi)核訪問用戶空間例程copy_to_user,。

 

Figure 4.1.5c Performing a read operation in the Linux driver

 

寫例程執(zhí)行和上面同樣的操作,,只是這一次數(shù)據(jù)是從用戶空間傳送到內(nèi)核空間,如圖4.1.5d,。

 

Figure 4.1.5d Performing a write operation in the Linux driver

 

設(shè)備控制例程

設(shè)備控制例程用來設(shè)置設(shè)備的各種狀態(tài),。應(yīng)用程序使用win32例程DeviceIoControl來對驅(qū)動程序進(jìn)行IOCTL調(diào)用。這個例程需要一個由驅(qū)動程序定義的IOCTL碼,。一個IOCTL碼告訴驅(qū)動程序應(yīng)用程序要執(zhí)行的操作,。在這個例子中,驅(qū)動實現(xiàn)IOCTL例程用來選擇當(dāng)前內(nèi)存塊號(current bank number),。驅(qū)動的IOCTL碼使用之前必須先被定義,。WindowsIOCTL碼的定義如圖4.1.6aCTL_CODE宏用來定義一個設(shè)備的IOCTL[Oney, 99]。CTL_CODE的第一個參數(shù)是設(shè)備ID,,ID數(shù)值范圍為0-65535,,其中0-32767為系統(tǒng)預(yù)留,32768-65535的使用可自定義,。所選的IOCTL碼必須和驅(qū)動的addDevice例程中為例程IoCreateDevice指定的設(shè)備編碼一致(參見節(jié)4.1.3addDevice例程所做的事情),。第二個參數(shù)為表示功能碼的12比特位長數(shù)值。02047Microsoft預(yù)留,,因此功能碼應(yīng)為大于2047而小于2^12,。這個通常用來表示哪個控制碼被定義,即將兩個IOCTL碼區(qū)分開,,如圖4.1.6a,。第三個參數(shù)值指定用于從用戶空間傳送參數(shù)到內(nèi)核空間的方法,第四個參數(shù)表示應(yīng)用程序?qū)υO(shè)備的訪問權(quán)限,。


Figure 4.1.6a IOCTL code definition in Windows

 

Linux下,,應(yīng)用程序使用系統(tǒng)例程ioctl來對驅(qū)動進(jìn)行IOCTL調(diào)用。IOCTL碼在文件Documentation/ioctl-numbers.txt中指定,,可在Linux內(nèi)核源碼樹中找到,。用于試驗的驅(qū)動選擇一個未使用的號碼,目前是大于0xFF的值,。這個驅(qū)動的IOCTL碼的定義如圖4.1.6b,。_IOWR表示數(shù)據(jù)將被傳送到或取自內(nèi)核空間。其它的宏如_IO表示沒有任何參數(shù),,_IOW表示數(shù)據(jù)僅將從用戶空間被傳送到內(nèi)核空間,,最后的_IOR表示數(shù)據(jù)僅將從內(nèi)核空間被傳送到用戶空間。以上的宏需要一個表示內(nèi)核和用戶空間所需要交換數(shù)據(jù)的大小的值,。據(jù)Rubini et al [Rubini et al, 01]建議,,為使驅(qū)動程序可移植性更好,這個值應(yīng)被設(shè)置為255(8比特),,雖然依賴于當(dāng)前架構(gòu)的數(shù)值為8-14比特位,。第二個參數(shù)和Windows下的函數(shù)編號類似,8位寬,,從0-255,。

 

Figure 4.1.6b IOCTL code definition in Windows

 

一旦IOCTL碼被選定,,IOCTL例程就可以被定義,。Windows下,IOCTL例程的定義如圖4.1.6c,。兩個IOCTL碼被處理,。第一個IOCTL_SELECT_BANK,設(shè)置當(dāng)前內(nèi)存塊號,第二個IOCTL_GET_VERSION_STRING,,返回驅(qū)動版本字符串,。從IOCTL例程返回的數(shù)據(jù)和readwrite請求返回的數(shù)據(jù)一樣被調(diào)用者處理,。

 

Figure 4.1.6c IOCTL routine definition in Windows

 

Linux下IOCTL例程的定義如圖4.1.6d,。對IOCTL碼的處理和Windows一樣,不同的只是語法上,。數(shù)據(jù)的處理和read,、write請求一樣。

 

Figure 4.1.6d IOCTL routine definition in Linux

 

PnP消息處理例程

Windows下,,PnP消息在適當(dāng)?shù)臅r候被分發(fā)給驅(qū)動程序,,如當(dāng)設(shè)備被添加到系統(tǒng)或被從系統(tǒng)移除時。這些消息被驅(qū)動程序?qū)崿F(xiàn)的PnP分發(fā)例程處理,。Linux下,,內(nèi)核并沒有發(fā)送PnP消息給驅(qū)動程序,因此也就沒有PnP例程,。Windows下PnP消息處理例程如圖4.1.7所示,。這個例子中,內(nèi)存設(shè)備驅(qū)動只處理其中一個PnP消息,。移除設(shè)備的消息是在驅(qū)動程序被系統(tǒng)卸載時被發(fā)送,。這個時候,通過調(diào)用I/O管理器例程IoSetDeviceInterface禁用驅(qū)動程序的接口,,驅(qū)動程序的功能設(shè)備對象和驅(qū)動程序分配的那些內(nèi)存塊也一并被刪除,。

 

Figure 4.1.7 PnP Message handler routine

 

驅(qū)動開發(fā)環(huán)境

為到此為止所討論的兩種操作系統(tǒng),即Microsoft Windows和Linux開發(fā)驅(qū)動需要使用特定于每個平臺的一些軟件開發(fā)工具,。Windows和Linux操作系統(tǒng)的內(nèi)核都是使用C語言編寫,,這使得為兩個系統(tǒng)編寫的驅(qū)動程序也跟著使用C語言編寫。Windows支持使用面向?qū)ο蟮木幊陶Z言C 來編寫驅(qū)動程序,,Linux卻沒有支持,。

 

Windows驅(qū)動開發(fā)環(huán)境

Microsoft Windows是一個具有所有權(quán)的商用的操作系統(tǒng),即它需要被購買來使用,。針對Windows,,存在若干商用的驅(qū)動程序開發(fā)環(huán)境。舉個例子,,如NuMega DriverStudio? Suit [Compuware, 01],,帶有類庫和驅(qū)動構(gòu)造向?qū)б暂o助驅(qū)動開發(fā),同時還集成了一個調(diào)試器允許驅(qū)動代碼的調(diào)試,。

 

Windows設(shè)備驅(qū)動開發(fā)包

Windows下驅(qū)動開發(fā)的標(biāo)準(zhǔn)途徑是從Microsoft獲取設(shè)備驅(qū)動開發(fā)包(DDK)和利用一些輔助工具進(jìn)行開發(fā),。最新版的DDK可從MSDN得到(The latest version of the DDK is available to Microsoft Software Development Network (MSDN) subscribers),,DDK包含開發(fā)驅(qū)動所需要的程序。DDK安裝程序會安裝一些批處理文件,,這些批處理文件會建立一個外殼窗口使得可以為Microsoft每個版本的操作系統(tǒng)開發(fā)驅(qū)動,。這次對Windows驅(qū)動架構(gòu)考查所使用的DDK 3590,具有為Windows ME,、2000,、XP.NET開發(fā)驅(qū)動的環(huán)境,。每個平臺都有兩個版本的開發(fā)環(huán)境,,一種叫checked版,調(diào)試符號被添加到驅(qū)動代碼中,,另外一種叫發(fā)布版,,這個版本的驅(qū)動程序沒有調(diào)試符號。發(fā)布版開發(fā)環(huán)境也是驅(qū)動產(chǎn)品最后使用的編譯環(huán)境,。

 

Windows驅(qū)動的Makefile

DDK外殼窗口打開后,,一個簡單的build命令就可以編譯驅(qū)動程序。Makefile定義了用來生成驅(qū)動程序的源代碼文件,。Makefile的條目在一個叫sources的文件中指定,,放在build命令所處的當(dāng)前目錄下。圖5.1.2顯示了用來生成一個簡單驅(qū)動程序的Makefile的格式,。環(huán)境變量TARGETNAME指定了生成驅(qū)動的名稱,。 這個例子中,驅(qū)動程序?qū)⒔衜ydrivers.sys,,Windows下的驅(qū)動都以.sys為后綴,。TARGETPATH指定驅(qū)動程序賴以生成的目標(biāo)代碼文件。 目錄obj下有一個文件_objects.mac,,定義了額外的目標(biāo)文件路徑,。Windows 2000中,checked版默認(rèn)的目標(biāo)文件路徑為objchk_w2k,,發(fā)布版默認(rèn)的目標(biāo)文件路徑為objfre_w2k,。INCLUDES指定了編譯驅(qū)動所需要的包含文件路徑,SOURCES指定驅(qū)動賴以生成的驅(qū)動源碼文件,。

 

Figure 5.1.2 A Makefile used for building a WDM driver with the Windows DDK

 

Windows DDK文檔和工具

Windows DDK包含組織良好的API文檔和驅(qū)動程序例子代碼,。初學(xué)者可從這里學(xué)到如何創(chuàng)建驅(qū)動程序。DDK還包含一些輔助驅(qū)動開發(fā)的實用工具程序,。其中一個是設(shè)備樹應(yīng)用程序,,列出了當(dāng)前所有已加載的在\\device命名空間以層級方式列出的驅(qū)動(3.1.2.4關(guān)于設(shè)備命名空間的討論),顯示每個驅(qū)動棧,、驅(qū)動所實現(xiàn)的例程和驅(qū)動對象內(nèi)存地址,。Windows DDK提供的其它工具中有一個用來生成INF文件的叫geninf,它生成驅(qū)動程序安裝需要的INF文件,,還有一個PnP驅(qū)動測試應(yīng)用程序用來測試驅(qū)動是否支持PnP,。

 

Linux驅(qū)動開發(fā)環(huán)境

Linux驅(qū)動開發(fā)環(huán)境和Windows不同,Linux下沒有和Windows DDK對應(yīng)的東西,,也就是說內(nèi)核創(chuàng)建者沒有提供Linux設(shè)備驅(qū)動開發(fā)包,,而是將內(nèi)核源碼對所有人公開。內(nèi)核源碼的頭文件就是開發(fā)驅(qū)動所需要的所有東西,。驅(qū)動程序使用GNU C編譯器,,即GCC,它也被用來編譯應(yīng)用程序,。和Windows類似,,通過Makefile文件指定驅(qū)動如何被編譯生成。

 

Linux驅(qū)動開發(fā)Makefile

一旦定義了Makefile,,使用簡單的make命令來生成驅(qū)動,。圖5.2.1顯示了一個用來生成叫mydriver的驅(qū)動程序的Makefile文件示例,源代碼文件為mydriver.c,。第一個條目是KERNELDIR,,定義一個環(huán)境變量,指定了內(nèi)核頭文件的位置,。后面一行包含了當(dāng)前內(nèi)核的配置信息,。在內(nèi)核和驅(qū)動程序被編譯生成之前,外部定義的內(nèi)核變量在.config文件中指定,,該文件位于內(nèi)核源碼樹的根目錄,,這樣內(nèi)核的頭文件可以使用這些信息。CFLAGS用來設(shè)置GCC編譯器額外的標(biāo)志,,‘-O’ 打開代碼優(yōu)化開關(guān),,‘-Wall’打印所有的代碼警告?!產(chǎn)ll’節(jié)是make命令執(zhí)行時默認(rèn)會去檢查的節(jié),。一個目標(biāo)叫mydriver,依賴于目標(biāo)文件mydriver.o,,mydriver.oGCC生成,。環(huán)境變量LD指定用來生產(chǎn)最后的驅(qū)動模塊的GNU鏈接器。選項‘-r’指定輸出可被重定位,,即里面的內(nèi)存內(nèi)置應(yīng)該是相對于某個基地址的偏移,,這個基地址在編譯的時候是不知道的?!?^’ 是mydriver.o的別名,,‘$@’ 是mydriver的別名,,也就是說,它要求鏈接器從mydriver.o目標(biāo)文件生成可重定位的代碼然后生成輸出文件mydriver,。

 

Figure 5.2.1 Makefile used to build a driver in Linux

 

內(nèi)核模塊管理程序如insmodlsmod分別用來將驅(qū)動程序加載到內(nèi)核和查看當(dāng)前已加載的所有內(nèi)核模塊,。

 

Linux驅(qū)動開發(fā)文檔

Linux內(nèi)核源碼樹下有個叫“Documentation”的目錄,這個目錄下有一些關(guān)于Linux內(nèi)核方面的文檔,,但還是沒有Windows DDK文檔那樣完整和生動,。Rubini et al[Rubini et al, 01]編寫Linux驅(qū)動書籍對設(shè)備驅(qū)動開發(fā)者來說是個更好的信息來源。Linux內(nèi)核沒有自帶任何驅(qū)動程序例子,,但有在實際環(huán)境中被使用的驅(qū)動程序源碼,,這些代碼可以作為開發(fā)新設(shè)備驅(qū)動的基礎(chǔ)。然而,,這對設(shè)備驅(qū)動開發(fā)新手來說并不是一個好的引導(dǎo)素材,。

 

驅(qū)動程序調(diào)試

每一個軟件部件在其開發(fā)周期內(nèi)總是不時地需要調(diào)試,因為總存在一些難以靠檢查源碼就能發(fā)現(xiàn)的晦澀的bug,。對驅(qū)動程序來說更是如此,。應(yīng)用程序的bugs最壞情況下會是應(yīng)用進(jìn)程不穩(wěn)定,而驅(qū)動程序中嚴(yán)重的bug會使整個系統(tǒng)不穩(wěn)定,。調(diào)試應(yīng)用程序很直觀,,即在調(diào)試器的幫助下,在感興趣的源代碼位置設(shè)置一個中斷語句,。這個因調(diào)試器而異,。Windows下,使用Microsoft Visual Studio調(diào)試器,,設(shè)置斷點只需要在源碼所在行點擊一下鼠標(biāo),。DDD(Linux下的GUI調(diào)試器,使用了最流行的命令行調(diào)試器GDB)調(diào)試器也是一樣的操作,。程序在調(diào)試模式運行時,,遇到斷點程序會暫停執(zhí)行使得可以單步跟蹤,即從那個地方開始的指令逐條執(zhí)行并可觀察執(zhí)行的效果,。調(diào)試器中一般可以看到被調(diào)試程序中變量的內(nèi)存地址和變量的值,。到此為止我們所討論的調(diào)試方式也適用于驅(qū)動程序的調(diào)試,某種程度上還適用于每種操作系統(tǒng),。

 

Windows下驅(qū)動調(diào)試

Windows下驅(qū)動程序的調(diào)試有若干不同的方法,。最簡單的就是使用DbgPrint調(diào)試?yán)虒⑾⒋蛴〉絎indows調(diào)試器緩沖區(qū)。比如使用Windbg調(diào)試器時,,可從調(diào)試器界面看到那些消息,,否則需要一個特定的程序從調(diào)試器緩沖區(qū)接收那些消息,如SysInternals公司免費提供的DebugView程序[Russinovich, 01],。DbgBreakPoint例程在程序中設(shè)置一個斷點,,當(dāng)被執(zhí)行時,,系統(tǒng)停下來并將驅(qū)動執(zhí)行代碼傳遞給系統(tǒng)調(diào)試器。Assert宏基于條件測試的結(jié)果,,將驅(qū)動執(zhí)行轉(zhuǎn)移到系統(tǒng)調(diào)試器,。使用Microsoft內(nèi)核調(diào)試器Windbg,需要兩個PC,。第一個PC是驅(qū)動代碼的開發(fā)和調(diào)試機器,,第二個PC通過串口連到開發(fā)驅(qū)動的PC,。開發(fā)者使用第二個PC,,通過串口控制臺連接到第一個PC,就能和在第一個PC上的調(diào)試器交互,。NuMega DriverStudio ? [Compuware, 01]提供的調(diào)試器允許驅(qū)動調(diào)試在單個PC內(nèi)部,,這個PC可以作為驅(qū)動開發(fā)機器,并作為應(yīng)用調(diào)試器,。它提供了一個console窗口,,命令行可從這里輸入以便進(jìn)行控制。

 

Linux下驅(qū)動調(diào)試

和Windows一樣,,Linux驅(qū)動調(diào)試可使用內(nèi)核提供的調(diào)試?yán)蘰rintk,,對應(yīng)于Windows的DbgPrint例程。它和C的標(biāo)準(zhǔn)I/O例程printf類似,,只是需要一個額外的參數(shù)來指定消息將被打印到的位置,。內(nèi)核調(diào)試器可作為內(nèi)核源碼的一個patch被獲取到。Linux內(nèi)核調(diào)試器(kdb)的patch可從KDB項目頁面獲取[KDB, 02],。它允許標(biāo)準(zhǔn)調(diào)試器一樣的操作,,即設(shè)置斷點、單步執(zhí)行驅(qū)動代碼和觀察驅(qū)動內(nèi)存,。

 

總結(jié)

WindowsLinux是當(dāng)今最為普遍流行的操作系統(tǒng),。Windows的市場份額最大,Linux知名度則在不斷增長,。硬件設(shè)備制造商每發(fā)布一種新設(shè)備,,都配備有一個能使新設(shè)備在Windows下使用的驅(qū)動程序。兩種操作系統(tǒng)的驅(qū)動架構(gòu)有很多的不同,,但也有一些類似的地方,。

 

設(shè)備驅(qū)動架構(gòu)

通過對兩種操作系統(tǒng)驅(qū)動架構(gòu)的比較,可以看到Windows系統(tǒng)的架構(gòu)更為成熟,。這并不意味著Windows架構(gòu)提供更好的功能,,而是它有一個定義更為良好的的驅(qū)動模型讓驅(qū)動開發(fā)者去遵循。雖然驅(qū)動程序的編寫者可以忽視Windows驅(qū)動模型開發(fā)自己的驅(qū)動,,但很少有驅(qū)動開發(fā)者這么做,。Linux下沒有正式定義的驅(qū)動模型,。Linux驅(qū)動程序編寫者基于他們個人的設(shè)計來開發(fā)驅(qū)動。除非兩個驅(qū)動開發(fā)組合作來開發(fā)能一起工作的驅(qū)動程序,,不同開發(fā)者開發(fā)的驅(qū)動程序在Linux系統(tǒng)下不能協(xié)同工作,。Windows下,兩個或多個驅(qū)動開發(fā)組開發(fā)的驅(qū)動可以協(xié)同工作,,只要他們都遵循WDM來構(gòu)建驅(qū)動程序,。Windows驅(qū)動架構(gòu)支持PnP和電源管理,是通過在適當(dāng)?shù)臅r候?qū)⑦@些消息發(fā)送到實現(xiàn)了消息處理函數(shù)的驅(qū)動,。目前的Linux驅(qū)動架構(gòu)沒有提供這樣的機制,。

 

設(shè)備驅(qū)動程序設(shè)計

設(shè)計驅(qū)動程序時應(yīng)該對操作系統(tǒng)提供的輔助設(shè)施進(jìn)行評估。Windows和Linux是兩個現(xiàn)代化的操作系統(tǒng),。它們提供了對數(shù)據(jù)結(jié)構(gòu)如棧(stack),、隊列(queue)和自旋鎖(spin locks),還有完成硬件無關(guān)操作的硬件抽象層(HAL,,Hardware Abstraction Layer)例程的實現(xiàn),。這使得驅(qū)動程序可以在不同的處理器架構(gòu)下運行,如IA64 (Intel’s 64 bit platform) 和SPARC,。兩種操作系統(tǒng)下的驅(qū)動程序都可以被分成多個模塊,,然后以棧式結(jié)構(gòu)堆疊,使用標(biāo)準(zhǔn)化的數(shù)據(jù)結(jié)果進(jìn)行通信,。Windows這種標(biāo)準(zhǔn)化的數(shù)據(jù)結(jié)構(gòu)就是IRP,,Linux下則可是任何驅(qū)動程序自定義的結(jié)構(gòu),因為操作系統(tǒng)沒有提供任何標(biāo)準(zhǔn)化的結(jié)構(gòu),。

 

設(shè)備驅(qū)動程序?qū)崿F(xiàn)

兩種操作系統(tǒng)下的驅(qū)動程序都由一系列各操作系統(tǒng)期望驅(qū)動程序?qū)崿F(xiàn)的例程組成,。包括標(biāo)準(zhǔn)I/O如讀寫設(shè)備的例程、發(fā)送I/O控制命令到設(shè)備的例程,。兩種系統(tǒng)中,,每個驅(qū)動程序都要實現(xiàn)一個驅(qū)動被加載時執(zhí)行的例程和驅(qū)動被卸載時執(zhí)行的例程,不同驅(qū)動程序的例程可使用相同的名稱,,但一般來說每種操作系統(tǒng)都使用慣用的命名方式,。Windows下設(shè)備驅(qū)動的的命名方式(設(shè)備接口)比當(dāng)前Linux下設(shè)備驅(qū)動的命名方式更便捷。Linux使用GUID為設(shè)備命名,,相比Windows,,驅(qū)動名稱沖突的現(xiàn)象在Linux下更易于出現(xiàn)。

 

驅(qū)動程序開發(fā)環(huán)境

Windows操作系統(tǒng)提供DDK,,其中包含相關(guān)的文檔和開發(fā)工具,,大大減少了開發(fā)驅(qū)動需要的學(xué)習(xí)時間。Linux下沒有DDK,因此剛開始時設(shè)備驅(qū)動開發(fā)者需要收集其他的一些資源來輔助驅(qū)動的開發(fā),。一旦花時間熟悉了兩種驅(qū)動開發(fā)環(huán)境后,,開發(fā)者會發(fā)現(xiàn)創(chuàng)建Linux驅(qū)動程序比創(chuàng)建Windows驅(qū)動程序更容易,因為所有的Linux內(nèi)核源碼對他們是可見的,。這使得驅(qū)動開發(fā)者能更深入到他們的驅(qū)動程序所依賴的內(nèi)核代碼中去跟蹤解決驅(qū)動程序的問題,。Windows,只有debug版二進(jìn)制組件可用,,里面包含調(diào)試符號如函數(shù)名稱和變量名,,但不如擁有操作系統(tǒng)源碼的用處那么大。

 

結(jié)束語

驅(qū)動程序應(yīng)該被設(shè)計成需要終端用戶很少的交換就能使用,,并且應(yīng)用程序可以訪問驅(qū)動的所有功能,。第一點是Windows的一個要點,它支持了PnP,。Linux是一個開源項目,,它還在不斷地改進(jìn)中,。以后Linux的驅(qū)動架構(gòu)很有可能像Windows驅(qū)動架構(gòu)那樣正式化,,如具有一個WDM那樣的驅(qū)動模型。隨著越來越多的個人和組織采用了Linux,,支持Linux的硬件廠商也會增加,。

 

致謝

This research was made possible through the Andrew Mellon Foundation scholarship at Rhodes University,

Grahamstown, South Africa.

 

參考書目

[Beck et al, 98] Beck, Bohme, Dziadzka, Kunitz, Magnus, Verworner, Linux Kernel Internals,

Addison Wesley, 1998.

[Cant C, 99] Cant C, Writing Windows WDM Device Drivers, CMP Books, 1999.

[Compuware, 01] Compuware, NuMega DriverStudio Version 2.5, http://www., 2001.

[Compuware, 01] Compuware, Using Driver Works, Version 2.5, Compuware, 2001.

[Deitel, 90] Deitel HM, Operating Systems, 2nd Edition, Addison Wesley, 1990.

[Davis, 83] Davis W, Operating Systems, 2nd Edition, Addison Wesley, 1983.

[Flynn et al, 91] Flynn IM, McHoes AM, Understanding Operating Systems, Brooks/Cole, 1991.

[Katzan, 73] Katzan H, Operating Systems: A Pragmatic Approach, Reinhold, 1973.

[KDB, 02] KDB, The Linux Built in Kernel Debugger, http://oss./projects/kdb, 2002.

[Laywer, 01] Lawyer D S, Plug and Play HOWTO/Plug-and-Play-HOWTO-1.html,

http://www./HOWTO, 2001.

[Linus FAQ, 02] The Rampantly Unofficial Linus Torvalds FAQ,

http://www./~esr/faqs/linus/index.html, 2002.

[Linux HQ, 02] The Linux Headquarters, http://www., 2002.

[Lorin et al, 81] Lorin H, Deitel HM, Operating systems, Addison Wesley, 1981.

[Microsoft DDK, 02] Microsoft ,DDK- Kernel Mode Driver Architecture, Microsoft, 2002.

[Microsoft WDM, 02] Microsoft, Introduction to the Windows Driver Model,

http://www.microsoft.com/hwdev/driver/wdm, 2002.

[Oney, 99] Oney W, Programming the Microsoft Windows Driver Model, Microsoft, 1999.

[Open Group, 97] Open Group, Universal Unique Identifier,

http://www./onlinepubs/9629399/apdxa.htm, 1997.

[Redhat,02] Redhat, http://www.,2002.

[Rubini et al, 01] Rubini A, Corbet J, Linux Device Drivers, 2nd Edition, Oreilly, 2001.

[Russinovich, 98] Russinovich M, Windows NT Architecture,

http://www./Articles/Index.cfm?ArticleID=2984, 1998.

[Russinovich, 01] Russinovich M, SysInternals, http://www., 2001.

[Rusling, 99] Rusling D A, The Linux Kernel, http://www./LDP/tlk/tlk.html, 1999.




    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多