磁碟的裝置驅動堆疊

2022-04-10 19:22:57 字數 3986 閱讀 5994

磁碟的裝置驅動堆疊

本文節選自《windows 核心情景分析--採用開源**reactos》一書

讀者已經在前幾節中看到,裝置的驅動常常分成「類裝置驅動」和「埠裝置驅動」兩層。例如滑鼠器就成為乙個裝置的類,而具體又有ps/2滑鼠器、串列埠滑鼠 器以及基於usb的hid滑鼠器,所以滑鼠器的驅動就分為一種類裝置驅動和三種埠裝置驅動。其中ps/2滑鼠器的埠驅動是直接與硬體打交道的。不過端 口驅動也可能不直接驅動硬體,而只是對虛擬的硬體進行操作。hid滑鼠器的埠驅動就是這樣,因為它與實際的硬體之間還隔著usb匯流排這一層,因而需要把 hid的埠裝置驅動堆疊在usb匯流排驅動的上面,usb匯流排驅動下面又是usb的類驅動和埠驅動。至於類裝置驅動與埠裝置驅動之間的關係,則既可以 是一對一的,也可以是一對多的。

類裝置驅動與埠裝置驅動的劃分有三方面的好處。一方面,像滑鼠器驅動那樣,可以把不同種類滑鼠器驅動中的公共部分抽取出來,避免開發滑鼠器驅動時的重複 勞動。這種重複勞動不僅是浪費的問題,而且還容易引起差錯和不相容。另一方面,即使實際上某種裝置並不成為「類」,把本來可以是「整塊式」的裝置驅動按其 邏輯和機理分成兩塊也有好處,因為那樣可以增進軟體的模組化,從而使這些模組的**多樣化。再說,類裝置驅動與埠裝置驅動的劃分,以及這二者在介面介面 形式上的規範性,還使得按需要在二者之間插入「過濾」模組成為可能。

所以類裝置驅動與埠裝置驅動的劃分是個重要的概念,也是一項重要的技術。

但是,就磁碟的驅動而言,類驅動和埠驅動的分離仍舊是不充分的,因為磁碟有邏輯和物理之分,邏輯磁碟很可能就是物理磁碟上的乙個分割槽,還可能實際上不是 磁碟。所以,在這樣的條件下,還會把類驅動再分成兩層,成為上下兩個裝置物件,上面的稱為「功能(性)裝置驅動(functional device object)」即fdo,下面的稱為「物理裝置驅動(physical device object)」即pdo。不過pdo未必就是直接與物理裝置打交道的裝置物件,而是(向上)代表著某種物理裝置的裝置物件,「代表著」不一定就是「直接 操作著」。有時候,fdo和pdo實際上就只是「上、下」之分。那麼,埠裝置驅動是否還可以進一步分解呢?

對於同一種裝置,來自不同廠家的硬體介面也會有些不同,但是這種不同只是區域性的、少量的,一般都集中在底層直接與硬體有關的地方,而離具體的硬體介面稍遠 就又都一樣了。以磁碟為例,首先「塊儲存裝置」形成乙個類,屬於這個類的有磁碟、磁帶、光碟,可能還有u盤、ramdisk等。而磁碟又有邏輯和物理之 分。因為乙個物理的磁碟可以分成好幾個分割槽,而每個分割槽就是乙個邏輯的磁碟。然後,物理的磁碟又有ide磁碟和scsi磁碟之分。最後,即使同為ide磁 盤,不同廠家的產品也會有些不同和特殊之處,例如ide介面的暫存器可以表現在只能通過in、out指令訪問的i/o位址空間,也可以表現在通過mov指 令訪問的記憶體位址空間;有些廠家還提供ide磁碟陣列,有些則可能還有特殊的操作要求等。如果要求每個磁碟廠家都必須提供全套的驅動即整個磁碟驅動堆疊, 那當然不現實。即使只要求提供整個埠驅動,那也會造成許多重複開發,並且也對磁碟廠家提出了更高的要求,增加了許多負擔。而如果能把其中公共的部分提取 出來,做成乙個共用的模組,使磁碟廠家可以只做與具體產品密切相關的那一部分驅動程式,那麼磁碟廠家的負擔就可以降到最低。這就是miniport驅動的 由來,mini既有「小」的意思,也有「最小化(minimalized)」的意思。而提取出來的公共部分,則仍稱為port驅動,本質上就是作業系統內 核的一部分,所以一般是由微軟自己提供的。「miniport」這個詞,按字面意義稱之為「小埠」或「最小埠」固然可以,但也不盡合適,因為沒有反映 出這種驅動模組的本質;筆者覺得稱之為「末梢埠」或許還可以,因為miniport都是在最底層直接與硬體打交道的。

再看上下層裝置物件之間的介面。在「類驅動+埠驅動」的模型中,二者之間的介面就是常規的由irp和iocalldriver()所構成的介面。當然, 載運在irp上的資料或資料結構是根據具體情況而定的,這需要上下兩個模組之間有個協議。而若把類驅動分為fdo和pdo兩層,則兩層之間的互動一般遠較 類驅動與埠驅動之間的互動更為複雜和緊密,此時光靠由irp和iocalldriver()構成的介面就不夠了。為此,在類驅動的fdo和pdo之間往 往有另外乙個介面,要由下層向上層提交乙個資料結構,通過該資料結構向上層「登記」有關的函式指標和資料。可是這樣一來又有了問題,因為下層驅動模組的裝 入理應在上層模組之前,既然如此,那下層模組又如何向尚未裝入的上層模組登記呢?於是,fdo中又得劃分出一部分,這一部分是需要在下層模組之前裝入的, 起著pdo與fdo之間的中介作用,使下層模組在初始化時可以預先登記。這一部分當然是個可以獨立裝入的模組,但是卻不建立自己的裝置物件,因為它的裝置 物件(如果要說實質上有的話)就是其所屬的類驅動的裝置物件,要到裝入時才會建立。如果我們以是否有裝置物件為依據把裝置驅動模組分成「有形」和「無形」 兩種,那麼這就是屬於無形的裝置驅動模組。就其本質而言,無形的驅動模組就相當於系統空間的dll,實質上成為核心的擴充。此外,應用軟體在開啟某個裝置 時只能以(有命名的)裝置物件為目標,所以無形的驅動模組不能作為應用軟體的開啟目標。

實際上,從類驅動中劃分出乙個無形的模組並不只是為了解決下層模組(pdo)向上層登記的問題,這裡面也有「提取公因子」的問題。比方說,磁碟本身構成一 類裝置即磁碟類,但是同時它又屬於乙個更大的類即塊儲存裝置類;而磁帶類同樣也屬於塊儲存裝置類。這二者顯然存在著一些共性。如果分別加以實現,則磁碟的 類驅動和磁帶的類驅動中勢必有一部分程式是共同的。既然有共同的部分,那就不如把它提取出來。事實上,classpnp.sys就是從windows的塊 儲存裝置的類驅動中抽取出來的公共部分。

類似的原理也適用於埠驅動與小埠驅動之間,這二者的裝置物件之間的介面既保留了irp+ iocalldriver(),又增加了基於由下層向上層「登記」的擴充介面。

所以,windows的這個以irp和iocalldriver()為特徵的模型,簡潔固然是簡潔,實際上卻並不能貫徹始終,對於比較複雜的裝置就不能愉快勝任了。而由下層向上層登記乙個資料結構,以提供函式指標和資料,則正是linux所採用的方法。

可以這樣來理解,基於irp和iocalldriver()的裝置物件堆疊構成特定裝置驅動的骨架,體現著總體上的層次關係;但是此骨架中的某些層次又可 以進一步劃分成「子層」,子層之間的介面並不完全遵循irp+iocalldriver()的模型。不過,「過濾驅動(filter driver)」只能插入在堆疊中的骨幹層次之間。

下面介紹scsi磁碟(不包括光碟)驅動的堆疊,但是我們把注意力集中在堆疊的構成以及類、埠、小埠驅動的相互關係,而不是集中在具體的操作細節上, 因為許多細節是因具體硬體而異的。我們假定所用的是scsi磁碟,而插在計算機匯流排上的介面即「介面卡(adapter)」,是adaptec的 1540b介面板。ddk提供了這種介面板的小埠驅動,但是卻並未提供scsi的埠驅動,所以下面的有些**取自ddk,有些卻取自reactos。 而在類驅動這一層上,ddk倒是既提供了disk.sys的**,也提供了classpnp.sys的**。

對於scsi磁碟,其驅動模組堆疊的構成為:

disk.sys。這是磁碟的類驅動,分為fdo和pdo兩個子層,這兩個子層各有自己的裝置物件。二者的結合把對於邏輯磁碟的操作對映到對於物理磁碟的 操作。如前所述,磁碟和磁帶等同屬於塊儲存裝置類,所以部分本來可以放在disk.sys中的公共**被提取了出來,成為classpnp.sys。 ddk提供了這個模組的原始碼。

classpnp.sys。這是個無形的模組,沒有自己的裝置物件,只是起著函式庫的作用。ddk提供了這個模組的原始碼。

scsiport.sys。scsi磁碟的埠驅動,這又是個無形的模組,沒有自己的裝置物件。ddk並未提供這個模組的原始碼,但是reactos已經實現了這個模組。

aha154x.sys。這是adaptec 1540b介面板的小埠驅動。ddk提供了這個模組的原始碼。

disk.sys和classpnp.sys這兩個驅動模組合在一起相當於乙個類驅動模組,可是分成兩塊以後稱為什麼呢?在ddk的原始碼中,前者的路徑是 src"storage"class"disk,似乎應該算是類驅動;可是後者既然名為classpnp.sys,就更應該是類驅動。然而,這二者的特性 顯然不同,前者是有形的,其所建立的裝置物件在裝置物件堆疊中,而後者只是個無形的函式庫,可見微軟對於什麼樣的驅動是類驅動並無明確的定義。不過,從後 面的**中可以看出,如果把classpnp.sys理解成對於「塊裝置」這個大類的驅動,而把disk.sys理解成對於「磁碟」這個子類的驅動,則也 還可以說得通。

現在我們可以讀**了。

編寫塊裝置驅動之記憶體模擬磁碟

可以參考一下核心裡面的檔案 在核心裡面收索blk init queue 然後就會發現xd.c和z2ram.c 大概看一眼,看乙個去驅動程式從入口開始看,這裡有個register blkdev註冊塊裝置驅動,跟我們字元裝置相比少了乙個fops,註冊塊裝置已經退化了,它只不過是cat prok devi...

裝置堆疊例項

為了詳細說明wdm裝置物件以及這些裝置物件是如何分層的。本文以針對usb硬體的驅動為例,看看裝置物件是如何建立的。下圖表明了裝置物件是如何被乙個驅動程式建立的。我們從最底下開始 該裝置棧中裝置物件如下所示 1 針對pci 匯流排建立的pdo physicdevice object 和fdo func...

裝置驅動例項 字元裝置驅動

在整個linux裝置驅動學習中,字元裝置驅動較為基礎。通過對它的學習,對裝置驅動進一步加深了解 cdev 結構體struct cdev 講下比較重要的成員變數 dev t dev 定義了32位的裝置號,其中12位是主裝置號,20位是從裝置號。獲取主裝置號 major dev t dev 獲取從裝置號...