Linux內(nèi)核中的設(shè)備模型及SCSI示例解析
關(guān)于硬件架構(gòu)
想要了解Linux操作系統(tǒng)的內(nèi)核設(shè)備和驅(qū)動模型,***先了解一下現(xiàn)在計算機(jī)硬件的架構(gòu)。對計算機(jī)硬件有一定了解之后,對理解Linux內(nèi)核中的設(shè)備和驅(qū)動模型非常有幫助。如圖1是常規(guī)計算機(jī)的硬件架構(gòu)簡圖。

圖1 計算機(jī)硬件架構(gòu)簡圖
這里面需要重點理解的概念包括:總線、PCI橋和設(shè)備三個概念。我們下面大概介紹一下這幾個概念的含義:
總線: 我們知道計算機(jī)通常包括幾大件,CPU、內(nèi)存、輸入設(shè)備和輸出設(shè)備等。這些設(shè)備之間進(jìn)行通信需要依賴一種通道,這個通道就是總線。說的直白寫,總線就是傳輸數(shù)據(jù)的通道,可以類比日常生活中的馬路,各個不同的城市通過馬路來交換物資??偩€有很多種,比如常見的PCI總線,ISA總線和I2C總線等等,我們這里就不相信介紹。
PCI橋: PCI橋是連接PCI總線的紐帶,其作用與網(wǎng)絡(luò)領(lǐng)域的網(wǎng)橋類似。其實我們平時說的北橋,就包含PCI橋。PCI橋主要分3種,3種橋的具體含義如下:
HOST/PCI橋:提供CPU和PCI設(shè)備相互訪問的通道,實現(xiàn)CPU空間和PCI空間的映射。
PCI-PCI橋:實現(xiàn)PCI設(shè)備的級聯(lián)。
PCI/ISA或LPC橋:實現(xiàn)對ISA設(shè)備的兼容。
設(shè)備:設(shè)備就是具體的設(shè)備了,比如網(wǎng)卡、鍵盤和鼠標(biāo)等等。
Linux中的設(shè)備軟件模型
為了降低設(shè)備多樣性帶來的Linux驅(qū)動開發(fā)的復(fù)雜度,以及設(shè)備熱拔插處理、電源管理等,Linux內(nèi)核提出了設(shè)備模型(也稱作Driver Model)的概念。設(shè)備模型將硬件設(shè)備歸納、分類,然后抽象出一套標(biāo)準(zhǔn)的數(shù)據(jù)結(jié)構(gòu)和接口。驅(qū)動的開發(fā),就簡化為對內(nèi)核所規(guī)定的數(shù)據(jù)結(jié)構(gòu)的填充和實現(xiàn)。Linux中的軟件概念與實際物理的概念有一個大致的對應(yīng)關(guān)系,在內(nèi)核中相關(guān)的概念主要包括Bus、Device、Device Driver和Class等。下面是Linux對上述概念的介紹:
Bus(總線):Linux認(rèn)為(可以參考include/linux/device.h中struct bus_type的注釋)總線是CPU和一個或多個設(shè)備之間信息交互的通道。而為了方便設(shè)備模型的抽象,所有的設(shè)備都應(yīng)連接到總線上。Linux總線是在上述物理總線基礎(chǔ)上做的抽象,它可以對應(yīng)物理總線,也可以沒有對應(yīng)物理總線。
Device(設(shè)備):抽象系統(tǒng)中所有的硬件設(shè)備,描述它的名字、屬性、從屬的Bus、從屬的Class等信息。
Device Driver(驅(qū)動):Linux設(shè)備模型用Driver抽象硬件設(shè)備的驅(qū)動程序,它包含設(shè)備初始化、電源管理相關(guān)的接口實現(xiàn)。而Linux內(nèi)核中的驅(qū)動開發(fā),基本都圍繞該抽象進(jìn)行(實現(xiàn)所規(guī)定的接口函數(shù))。
Class(分類):在Linux設(shè)備模型中,Class的概念非常類似面向?qū)ο蟪绦蛟O(shè)計中的Class(類),它主要是集合具有相似功能或?qū)傩缘脑O(shè)備,這樣就可以抽象出一套可以在多個設(shè)備之間共用的數(shù)據(jù)結(jié)構(gòu)和接口函數(shù)。因而從屬于相同Class的設(shè)備的驅(qū)動程序,就不再需要重復(fù)定義這些公共資源,直接從Class中繼承即可。
設(shè)備模型的核心思想
前面介紹了Linux的設(shè)備軟件模型相關(guān)的概念,下面介紹一下各種概念間的關(guān)系。對于Linux來說,其軟件層面的模型與硬件基本是一致的。由圖1, 如果把CPU和內(nèi)存開成一個樹根的話,整個計算機(jī)的設(shè)備間的關(guān)系其實類似一個樹,總線類似于樹枝。Linux內(nèi)核在具體實現(xiàn)的時候也是按照此規(guī)律進(jìn)行的,***層的是根總線(bus),然后是各種具體類型的總線(bus_type),而其下則是設(shè)備(device)。

圖2 Linux內(nèi)核驅(qū)動關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
如圖2所示,Linux內(nèi)核針對上面介紹的概念,實現(xiàn)了具體的數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)結(jié)構(gòu)的名稱基本與硬件類型名稱一致。比如bus_type表示某種類型的總線,device表示一個物理設(shè)備等。
設(shè)備和驅(qū)動: 用Device(struct device)和Device Driver(struct device_driver)兩個數(shù)據(jù)結(jié)構(gòu),分別從“有什么用”和“怎么用”兩個角度描述硬件設(shè)備。這樣就統(tǒng)一了編寫設(shè)備驅(qū)動的格式,使驅(qū)動開發(fā)從論述題變?yōu)樘羁阵w,從而簡化了設(shè)備驅(qū)動的開發(fā)。
總線與設(shè)備: 通過"Bus-->Device”類型的樹狀結(jié)構(gòu)解決設(shè)備之間的依賴,而這種依賴在開關(guān)機(jī)、電源管理等過程中尤為重要。
試想,一個設(shè)備掛載在一條總線上,要啟動這個設(shè)備,必須先啟動它所掛載的總線。很顯然,如果系統(tǒng)中設(shè)備非常多、依賴關(guān)系非常復(fù)雜的時候,無論是內(nèi)核還是驅(qū)動的開發(fā)人員,都無力維護(hù)這種關(guān)系。
而設(shè)備模型中的這種樹狀結(jié)構(gòu),可以自動處理這種依賴關(guān)系。啟動某一個設(shè)備前,內(nèi)核會檢查該設(shè)備是否依賴其它設(shè)備或者總線,如果依賴,則檢查所依賴的對象是否已經(jīng)啟動,如果沒有,則會先啟動它們,直到啟動該設(shè)備的條件具備為止。而驅(qū)動開發(fā)人員需要做的,就是在編寫設(shè)備驅(qū)動時,告知內(nèi)核該設(shè)備的依賴關(guān)系即可。
類: 使用Class結(jié)構(gòu),在設(shè)備模型中引入面向?qū)ο蟮母拍睿@樣可以***限度地抽象共性,減少驅(qū)動開發(fā)過程中的重復(fù)勞動,降低工作量。在Linux內(nèi)核驅(qū)動中,類是對具有共性的設(shè)備的抽象,比如顯示設(shè)備類,音頻設(shè)備類和SCSI設(shè)備類等等。比如SCSI設(shè)備類包括磁盤設(shè)備、光驅(qū)設(shè)備和USB設(shè)備等。
即插即用: 在現(xiàn)代操作系統(tǒng)中即插即用成為常態(tài),我們普通PC的U盤、光驅(qū)等都是即插即用的。而對于企業(yè)級的服務(wù)器甚至要求CPU和內(nèi)存等組件都是可以即插即用的。
即插即用的實現(xiàn)同樣借用Device和Device Driver兩個數(shù)據(jù)結(jié)構(gòu)。在Linux內(nèi)核中,只要任何Device和Device Driver具有相同的名字,內(nèi)核就會執(zhí)行Device Driver結(jié)構(gòu)中的初始化函數(shù)(probe),該函數(shù)會初始化設(shè)備,使其為可用狀態(tài)。
而對大多數(shù)熱拔插設(shè)備而言,它們的Device Driver一直存在內(nèi)核中。當(dāng)設(shè)備沒有插入時,其Device結(jié)構(gòu)不存在,因而其Driver也就不執(zhí)行初始化操作。當(dāng)設(shè)備插入時,內(nèi)核會創(chuàng)建一個Device結(jié)構(gòu)(名稱和Driver相同),此時就會觸發(fā)Driver的執(zhí)行。這就是即插即用的概念。
SCSI設(shè)備示例
SCSI設(shè)備是Linux內(nèi)核中支持的眾多設(shè)備中的一種。SCSI設(shè)備也遵循上面介紹的設(shè)備、驅(qū)動和總線的結(jié)構(gòu),但略有不同。Linux內(nèi)核中抽象了一個稱謂SCSI總線的虛擬總線。而在SCSI總線上又包含SCSI的驅(qū)動和設(shè)備。

圖3 SCSI體系結(jié)構(gòu)
SCSI整個架構(gòu)分為3層,其中中間是中間層,用于實現(xiàn)SCSI的公共功能,比如錯誤處理等。而上面一層稱謂高層,它代表各種scsi設(shè)備類型的驅(qū)動,如scsi磁盤驅(qū)動,scsi磁帶驅(qū)動,高層驅(qū)動認(rèn)領(lǐng)低層驅(qū)動發(fā)現(xiàn)的scsi設(shè)備,為這些設(shè)備分配名稱,將對設(shè)備的IO轉(zhuǎn)換為scsi命令,交由低層驅(qū)動處理。而最下面的稱謂底層,它代表與SCSI的物理接口的實際驅(qū)動器,主要為各個廠商為其特定的主機(jī)適配器(Host Bus Adapter, HBA)驅(qū)動,例如: FC卡驅(qū)動、SAS卡驅(qū)動和iSCSI(iSCSI可以使硬件HBA卡或者基于普通網(wǎng)卡的軟件實現(xiàn))等。
在圖3中,Disk Driver就是一個SCSI磁盤驅(qū)動,通過該驅(qū)動對用戶呈現(xiàn)一個普通的磁盤。中間層的驅(qū)動是必須***個被內(nèi)核加載的,如果編譯成內(nèi)核模塊的話,該內(nèi)核模塊為scsi_mod。然后是上層的驅(qū)動和底層的驅(qū)動。以SCSI磁盤為例,加載的模塊是sd_mod。
在SCSI中實現(xiàn)對應(yīng)上述概念的結(jié)構(gòu)體包括scsi_driver、scsi_device和SCSI類型的總線(bus)。其中SCSI類型的總線并沒有定義一個特別的數(shù)據(jù)結(jié)構(gòu)體,而是對bus_type數(shù)據(jù)結(jié)構(gòu)的實例化。
需要說明的是對于SCSI設(shè)備,其實現(xiàn)又是比較復(fù)雜的。我們以光纖適配卡為例,其中一個適配卡又包含多個通路,而每個通路同網(wǎng)絡(luò)的方式可以跟多個存儲設(shè)備連接。因此,對于SCSI設(shè)備來說,實現(xiàn)上要復(fù)雜很多。

圖4 光纖適配卡
在內(nèi)核中通過Scsi_Host、scsi_target等結(jié)構(gòu)體表示上述概念。具體細(xì)節(jié)本文不再詳述,后面我們再詳細(xì)介紹SCSI體系架構(gòu)、FC相關(guān)流程和iSCSI相關(guān)流程。