版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第6章第硬件和設(shè)備驅(qū)動(dòng)程序第6章硬件和設(shè)備驅(qū)動(dòng)程序
第一節(jié)總線第二節(jié)設(shè)備訪問(wèn)方式第三節(jié)設(shè)備驅(qū)動(dòng)程序第四節(jié)編寫Linux下的設(shè)備驅(qū)動(dòng)程序
6.1總線6.1.1總線概述總線是將計(jì)算機(jī)中不同類型的硬件組織在一起,并為它們提供通訊保證的計(jì)算機(jī)關(guān)鍵硬件。總線定義了硬件之間進(jìn)行通訊的“協(xié)議”,遵循同一種協(xié)議的硬件可在同一條總線上協(xié)調(diào)工作。物理上,總線由計(jì)算機(jī)主板上傳送信號(hào)的線路以及附屬的控制芯片組成。各種設(shè)備大多以控制卡的形式插在總線槽上??偩€協(xié)議包括一些物理上的約定,例如電氣特性以及控制卡的尺寸等。另外,總線協(xié)議也定義了總線上的信號(hào)時(shí)序以及總線的最大數(shù)據(jù)傳輸速率(以MHz為單位)等特性。不同的協(xié)議形成了不同的總線。在流行PC機(jī)中,常見(jiàn)的總線類型如表6.1所示。表6.1PC中的常見(jiàn)總線類型名稱說(shuō)明ISA總線ISA是“IndustyStandardArchitecture”的英文縮寫。ISA總線最初用于IBM的PCAT機(jī)中,一次可傳輸16位數(shù)據(jù),最大的數(shù)據(jù)傳輸率為5MB/sec。
VESA局部總線VESA是“VideoElectronicsStandardsAssociation視頻電子標(biāo)準(zhǔn)協(xié)會(huì)”的英文縮寫。VESA局部總線可提供處理器和視頻卡之間的高速數(shù)據(jù)傳輸。
EISA總線EISA是“ExtendedIndustyStandardArchitecture擴(kuò)展工業(yè)標(biāo)準(zhǔn)結(jié)構(gòu)”的英文縮寫。EISA總線的傳輸速率為30MB/sec。但該總線很少使用。
PCI總線PCI是“PeripheralComponentInterconnection,外設(shè)組件互連”的英文縮寫。PCI是最新的高性能總線。它的時(shí)鐘頻率為33MHz,一次可傳輸64位數(shù)據(jù)。
Linux不支持微通道結(jié)構(gòu)總線MCA(MicroChannelArchitecture)。Linux不支持微通道結(jié)構(gòu)總線MCA(MicroChannelArchitecture)。外設(shè)組件互連(PeripheralComponentInterconnection,PCI)是一種將系統(tǒng)中外部設(shè)備以結(jié)構(gòu)化與可控制方式連接到起來(lái)的總線標(biāo)準(zhǔn),包括系統(tǒng)部件連接的電氣特性及行為。PCI總線現(xiàn)在廣泛使用在基于Pentium的計(jì)算機(jī)中,一方面是因?yàn)樵摽偩€的數(shù)據(jù)吞吐量大,另一方面是因?yàn)樵摽偩€和具體的處理器無(wú)關(guān),也就是說(shuō),同一個(gè)PCI設(shè)備,既可以使用在i386計(jì)算機(jī)中,也可以使用在AlphaAXP計(jì)算機(jī)中。PCI總線的設(shè)計(jì)也使各種PCI外設(shè)卡可即插即用,但即插即用需要操作系統(tǒng)軟件額外的支持。6.1.2Linux對(duì)PCI總線的支持1.PCI總線的結(jié)構(gòu)6.1.2Linux對(duì)PCI總線的支持1.PCI總線的結(jié)構(gòu)圖6.1是一個(gè)簡(jiǎn)單的PCI系統(tǒng)的邏輯示意圖。每條PCI總線上的設(shè)備數(shù)目是有一定限制的,因此該系統(tǒng)使用PCI-PCI橋?qū)⒉煌腜CI總線粘合在一起。不同的PCI總線有唯一的編號(hào),CPU處于PCI0號(hào)總線上。利用PCI-ISA橋可在PCI總線上連接ISA總線。利用PCI-PCI橋和PCI-ISA橋,可以突破單個(gè)PCI總線的限制而連接許多設(shè)備。一般來(lái)說(shuō),服務(wù)器和桌面計(jì)算機(jī)的PCI總線拓?fù)浣Y(jié)構(gòu)是不同的。圖6.1簡(jiǎn)單的PCI系統(tǒng)邏輯示意圖ISA設(shè)備有兩種地址空間,一種是I/O端口空間,另一種是存儲(chǔ)器空間。和ISA設(shè)備相比較,PCI設(shè)備有三種地址空間,分別是I/O端口空間、存儲(chǔ)器空間和配置空間。不同的CPU類型對(duì)地址空間類型的支持不同,i386處理器的I/O端口和存儲(chǔ)器空間是分開(kāi)的,利用不同的處理器指令操作這些地址;AlphaAXP處理器卻只有存儲(chǔ)器空間,也就是說(shuō),端口的輸入輸出和內(nèi)存訪問(wèn)的指令是一樣的。因此,AlphaAXP處理器利用一種獨(dú)特的內(nèi)存映射方式將部分虛擬地址空間映射為PCI的地址空間。ISA設(shè)備有兩種地址空間,一種是I/O端口空間,另一種是存儲(chǔ)器空間。和ISA設(shè)備相比較,PCI設(shè)備有三種地址空間,分別是I/O端口空間、存儲(chǔ)器空間和配置空間。不同的CPU類型對(duì)地址空間類型的支持不同,i386處理器的I/O端口和存儲(chǔ)器空間是分開(kāi)的,利用不同的處理器指令操作這些地址;AlphaAXP處理器卻只有存儲(chǔ)器空間,也就是說(shuō),端口的輸入輸出和內(nèi)存訪問(wèn)的指令是一樣的。因此,AlphaAXP處理器利用一種獨(dú)特的內(nèi)存映射方式將部分虛擬地址空間映射為PCI的地址空間。PCI的前兩種地址空間和ISA的兩種地址空間類似,設(shè)備驅(qū)動(dòng)程序利用這些地址空間在系統(tǒng)和設(shè)備之間通訊。除此之外,PCI設(shè)備,包括PCI-PCI橋和PCI-ISA橋都包含稱為“配置頭”的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)包含在PCI設(shè)備的配置空間中。配置頭在配置空間中的位置和相應(yīng)的PCI插槽相關(guān),系統(tǒng)可以利用這些配置頭來(lái)判斷相應(yīng)的PCI插槽中是否存在PCI設(shè)備。圖6.2是PCI設(shè)備頭的結(jié)構(gòu)示意圖。一般來(lái)說(shuō),設(shè)備頭的長(zhǎng)度為256字節(jié),利用與特定硬件相關(guān)的代碼可讀取這些設(shè)備頭。根據(jù)其中的信息,操作系統(tǒng)可為每個(gè)已插入PCI插槽的PCI設(shè)備分配PCII/O端口的數(shù)量、地址,以及PCI存儲(chǔ)器的長(zhǎng)度和起始位置(由基地址寄存器定義),也可以配置PCI設(shè)備的中斷以及中斷請(qǐng)求線。但是只有專門為特定硬件系統(tǒng)設(shè)計(jì)的配置代碼才可以讀取和設(shè)置這些信息,在i386系統(tǒng)中,這種代碼通常由系統(tǒng)BIOS提供。詳細(xì)信息可參見(jiàn)有關(guān)的PCI總線規(guī)范。
圖6.2PCI設(shè)備的設(shè)備頭結(jié)構(gòu)2.Linux中PCI設(shè)備的初始化Linux中的PCI設(shè)備初始代碼可劃分為如下三個(gè)部分:PCIBIOS、PCI修正和PCI設(shè)備驅(qū)動(dòng)程序。PCIBIOS由一組標(biāo)準(zhǔn)的PCI設(shè)備訪問(wèn)功能函數(shù)組成,這些代碼對(duì)不同的平臺(tái)來(lái)說(shuō)是一樣的。PCI修正部分為非Intel的系統(tǒng)所特有。Intel的系統(tǒng)引導(dǎo)時(shí),可由系統(tǒng)BIOS完整配置PCI系統(tǒng),而對(duì)AlphaAXP系統(tǒng)來(lái)說(shuō),需要利用PCI修正代碼來(lái)完成PCI系統(tǒng)的配置。PCI設(shè)備驅(qū)動(dòng)程序?qū)嶋H并不是一個(gè)真正的設(shè)備驅(qū)動(dòng)程序,它只在引導(dǎo)時(shí)由操作系統(tǒng)調(diào)用進(jìn)行PCI設(shè)備的初始化。本章主要討論P(yáng)CI設(shè)備驅(qū)動(dòng)程序。PCI設(shè)備驅(qū)動(dòng)程序利用PCIBIOS的功能函數(shù)在系統(tǒng)中掃描所有的PCI設(shè)備(包括兩種橋設(shè)備),并建立如圖6.3所示的數(shù)據(jù)結(jié)構(gòu)。圖6.3中的數(shù)據(jù)結(jié)構(gòu)與圖6.1所示的PCI系統(tǒng)相對(duì)應(yīng)。PCI初始化代碼首先掃描PCI0號(hào)總線,為每個(gè)可能的PCI插槽讀取設(shè)備頭中的制造商標(biāo)識(shí)和設(shè)備標(biāo)識(shí)。如果發(fā)現(xiàn)一個(gè)已填充有效標(biāo)識(shí)信息的設(shè)備頭,則說(shuō)明對(duì)應(yīng)的PCI插槽中有合法的PCI設(shè)備,因此,系統(tǒng)為該設(shè)備建立一個(gè)pci_dev結(jié)構(gòu)。同一總線上所有的pci_dev數(shù)據(jù)結(jié)構(gòu)形成了一個(gè)pci_devices鏈表。PCI設(shè)備驅(qū)動(dòng)程序利用PCIBIOS的功能函數(shù)在系統(tǒng)中掃描所有的PCI設(shè)備(包括兩種橋設(shè)備),并建立如圖6.3所示的數(shù)據(jù)結(jié)構(gòu)。圖6.3中的數(shù)據(jù)結(jié)構(gòu)與圖6.1所示的PCI系統(tǒng)相對(duì)應(yīng)。PCI初始化代碼首先掃描PCI0號(hào)總線,為每個(gè)可能的PCI插槽讀取設(shè)備頭中的制造商標(biāo)識(shí)和設(shè)備標(biāo)識(shí)。如果發(fā)現(xiàn)一個(gè)已填充有效標(biāo)識(shí)信息的設(shè)備頭,則說(shuō)明對(duì)應(yīng)的PCI插槽中有合法的PCI設(shè)備,因此,系統(tǒng)為該設(shè)備建立一個(gè)pci_dev結(jié)構(gòu)。同一總線上所有的pci_dev數(shù)據(jù)結(jié)構(gòu)形成了一個(gè)pci_devices鏈表。如果在搜索0號(hào)總線時(shí)發(fā)現(xiàn)PCI-PCI橋設(shè)備,則系統(tǒng)會(huì)建立一個(gè)pci_bus結(jié)構(gòu),并和代表PCI0號(hào)總線的pci_bus結(jié)構(gòu)以及pci_dev結(jié)構(gòu)一起形成一個(gè)由pci_root所指的樹(shù)形結(jié)構(gòu)。接下來(lái),初始化代碼將繼續(xù)在次總線上掃描其他PCI設(shè)備,直到掃描完所有的PCI設(shè)備為止。如果在次總線上發(fā)現(xiàn)有PCI-PCI橋設(shè)備,則初始化代碼會(huì)繼續(xù)掃描次總線。圖6.3Linux內(nèi)核中的PCI數(shù)據(jù)結(jié)構(gòu)6.2
設(shè)備訪問(wèn)方式
6.2.1
查詢與中斷外設(shè)的操作通常耗時(shí)較長(zhǎng),因此,當(dāng)CPU實(shí)際執(zhí)行了命令指令之后,驅(qū)動(dòng)程序可采用兩種方式等待外設(shè)完成操作。一種是查詢方式,驅(qū)動(dòng)程序在提交命令之后就開(kāi)始查詢?cè)O(shè)備的狀態(tài)寄存器,當(dāng)狀態(tài)寄存器表明操作完成時(shí),驅(qū)動(dòng)程序可繼續(xù)后續(xù)處理。另一種方式是利用中斷,驅(qū)動(dòng)程序提交命令之后立即進(jìn)入休眠狀態(tài)(即放棄CPU的使用權(quán),而其他進(jìn)程就有機(jī)會(huì)運(yùn)行了),設(shè)備結(jié)束操作之后,會(huì)產(chǎn)生中斷信號(hào),操作系統(tǒng)則根據(jù)設(shè)備的中斷信號(hào)負(fù)責(zé)喚醒驅(qū)動(dòng)程序。顯然,查詢方式白白浪費(fèi)了大量的CPU時(shí)間,而中斷方式才是多任務(wù)操作系統(tǒng)中最有效利用CPU的方式。
圖6.432位i386PC的中斷處理硬件查詢方式實(shí)際是同步驅(qū)動(dòng)設(shè)備的方式,而中斷方式實(shí)際是異步驅(qū)動(dòng)設(shè)備的方式,利用中斷,系統(tǒng)可更加有效地利用CPU。(1)對(duì)某些設(shè)備,例如軟盤驅(qū)動(dòng)器,其IRQ是固定的。軟盤驅(qū)動(dòng)器的IRQ為6。(2)設(shè)備驅(qū)動(dòng)程序可通過(guò)探測(cè)程序自動(dòng)探測(cè)某些ISA設(shè)備的IRQ。(3)不能自動(dòng)探測(cè)的ISA設(shè)備,可通過(guò)啟動(dòng)參數(shù)指定設(shè)備的IRQ。(4)對(duì)PCI設(shè)備,設(shè)備的配置頭信息中已經(jīng)存在有相應(yīng)的設(shè)備IRQ,設(shè)備驅(qū)動(dòng)程序只需讀取該配置信息。查詢方式實(shí)際是同步驅(qū)動(dòng)設(shè)備的方式,而中斷方式實(shí)際是異步驅(qū)動(dòng)設(shè)備的方式,利用中斷,系統(tǒng)可更加有效地利用CPU。(1)對(duì)某些設(shè)備,例如軟盤驅(qū)動(dòng)器,其IRQ是固定的。軟盤驅(qū)動(dòng)器的IRQ為6。(2)設(shè)備驅(qū)動(dòng)程序可通過(guò)探測(cè)程序自動(dòng)探測(cè)某些ISA設(shè)備的IRQ。(3)不能自動(dòng)探測(cè)的ISA設(shè)備,可通過(guò)啟動(dòng)參數(shù)指定設(shè)備的IRQ。(4)對(duì)PCI設(shè)備,設(shè)備的配置頭信息中已經(jīng)存在有相應(yīng)的設(shè)備IRQ,設(shè)備驅(qū)動(dòng)程序只需讀取該配置信息。對(duì)ISA設(shè)備IRQ的探測(cè)過(guò)程如下。設(shè)備驅(qū)動(dòng)程序要求設(shè)備完成某項(xiàng)可導(dǎo)致中斷的操作,然后,系統(tǒng)打開(kāi)所有未被分配的中斷。如果設(shè)備產(chǎn)生中斷,系統(tǒng)將接收到該中斷,然后通過(guò)讀取中斷控制器的狀態(tài)寄存器可得知該中斷對(duì)應(yīng)的IRQ,它就是ISA設(shè)備的IRQ。當(dāng)然,這種探測(cè)方式在某些情況下是無(wú)法正常工作的。例如,當(dāng)ISA設(shè)備的I/O地址空間也未知時(shí)就無(wú)法利用這種方式。在配置ISA網(wǎng)絡(luò)卡的時(shí)候,這種情況經(jīng)常發(fā)生。這時(shí)就需要利用啟動(dòng)參數(shù)指定設(shè)備的IRQ和I/O端口地址。對(duì)PCI設(shè)備,由于可方便獲得設(shè)備配置頭信息中的IRQ,因此設(shè)置工作相對(duì)容易些。但因?yàn)镻CI設(shè)備只能在四個(gè)引腳(分別為A、B、C和D)上產(chǎn)生中斷,所以當(dāng)系統(tǒng)中有四個(gè)以上的PCI設(shè)備時(shí),就會(huì)發(fā)生IRQ重疊的情況。這時(shí),irq_action數(shù)組中的某個(gè)元素會(huì)同時(shí)指向多個(gè)irqaction結(jié)構(gòu),而當(dāng)產(chǎn)生該IRQ中斷時(shí),Linux會(huì)依次調(diào)用每個(gè)irqaction指定的中斷處理程序。在設(shè)備驅(qū)動(dòng)程序的中斷處理過(guò)程中,設(shè)備驅(qū)動(dòng)程序讀取設(shè)備狀態(tài)寄存器的值,從而可了解操作結(jié)果。在某些特定情況下,設(shè)備驅(qū)動(dòng)程序還要進(jìn)行其他處理,但中斷處理過(guò)程中只讀取設(shè)備狀態(tài),以便能夠快速結(jié)束中斷處理。如果還要進(jìn)行其他處理,會(huì)通過(guò)內(nèi)核機(jī)制在適當(dāng)?shù)臅r(shí)候進(jìn)行。如何將中斷發(fā)送給CPU取決于體系結(jié)構(gòu),但是在多數(shù)體系結(jié)構(gòu)中,中斷以一種特殊模式發(fā)送同時(shí)還將阻止系統(tǒng)中其它中斷的產(chǎn)生。設(shè)備驅(qū)動(dòng)在其中斷處理過(guò)程中作的越少越好,這樣Linux核心將能很快的處理完中斷并返回中斷前的狀態(tài)中。為了在接收中斷時(shí)完成大量工作,設(shè)備驅(qū)動(dòng)必須能夠使用核心的底層處理例程或者任務(wù)隊(duì)列來(lái)對(duì)以后需要調(diào)用的那些例程進(jìn)行排隊(duì)。6.2.2直接內(nèi)存訪問(wèn)利用中斷,系統(tǒng)和設(shè)備之間可以通過(guò)設(shè)備驅(qū)動(dòng)程序傳送數(shù)據(jù),但是,當(dāng)傳送數(shù)據(jù)量很大時(shí),因?yàn)橹袛嗵幚砩系难舆t,利用中斷的方式就不太有效了。例如,SCSI硬盤可在1秒之內(nèi)傳輸50MB的數(shù)據(jù),而通常的中斷延遲(中斷產(chǎn)生到相應(yīng)的設(shè)備驅(qū)動(dòng)程序被調(diào)用之間的時(shí)間)大約為2ms,顯然,利用中斷對(duì)整體數(shù)據(jù)傳輸速度的影響是非常大的。直接內(nèi)存訪問(wèn)(DirectMemoryAccess,DMA)控制器可以在不受處理器干預(yù)的情況下在設(shè)備和系統(tǒng)內(nèi)存之間高速傳輸數(shù)據(jù)。PC機(jī)的ISADMA控制器有8個(gè)DMA通道,其中七個(gè)可以由設(shè)備驅(qū)動(dòng)使用。每個(gè)DMA通道具有一個(gè)16位的地址寄存器和一個(gè)16位的記數(shù)寄存器,可分別用來(lái)定義一次DMA操作的系統(tǒng)內(nèi)存起始地址和內(nèi)存大小。設(shè)備驅(qū)動(dòng)程序在利用DMA之前,需要選擇DMA通道并定義上述寄存器數(shù)據(jù)傳輸方向以及讀寫類型,然后將設(shè)備設(shè)定為利用該DMA通道傳輸數(shù)據(jù)。設(shè)備完成操作之后,立即就可以利用該DMA通道在設(shè)備和系統(tǒng)內(nèi)存之間傳輸數(shù)據(jù)。傳輸完畢之后產(chǎn)生中斷以便通知設(shè)備驅(qū)動(dòng)程序進(jìn)行后續(xù)處理。利用DMA進(jìn)行數(shù)據(jù)傳輸?shù)耐瑫r(shí),CPU可以轉(zhuǎn)去執(zhí)行其他任務(wù)。在利用DMA進(jìn)行數(shù)據(jù)傳輸時(shí),有幾個(gè)需要注意的問(wèn)題:(1)DMA地址寄存器代表系統(tǒng)物理地址的低16位,加上頁(yè)寄存器中的高8位地址,DMA所能訪問(wèn)的地址限制在低16MB(早期的DMA控制器限制更嚴(yán)重)。(2)因?yàn)樵贒MA操作時(shí),CPU仍然在執(zhí)行指令,因此要防止寫入的物理內(nèi)存區(qū)域被虛擬內(nèi)存機(jī)制交換到交換空間中,這可通過(guò)鎖定相應(yīng)的物理頁(yè)避免。(3)DMA通道是有限資源。有些設(shè)備固定使用同一個(gè)通道,例如,軟盤驅(qū)動(dòng)器使用通道2;有些設(shè)備可通過(guò)硬件跳線選擇DMA通道;另外一些設(shè)備則更加靈活,它可以使用任何一個(gè)空閑的DMA通道,為此,Linux內(nèi)核為每個(gè)DMA通道維護(hù)一個(gè)dma_chan數(shù)據(jù)結(jié)構(gòu),根據(jù)該結(jié)構(gòu)中的信息可判斷指定的DMA通道是否已被分配。Linux通過(guò)dma_chan(每個(gè)DMA通道一個(gè))數(shù)組來(lái)跟蹤DMA通道的使用情況。dma_chan結(jié)構(gòu)中包含有兩個(gè)域,一個(gè)是指向此DMA通道擁有者的指針,另一個(gè)指示DMA通道是否已經(jīng)被分配出去。當(dāng)敲入cat/proc/dma打印出來(lái)的結(jié)果就是dma_chan結(jié)構(gòu)數(shù)組。
6.2.3
內(nèi)存系統(tǒng)接收到中斷信號(hào)時(shí)或調(diào)度底層任務(wù)隊(duì)列處理過(guò)程時(shí),設(shè)備驅(qū)動(dòng)開(kāi)始運(yùn)行。設(shè)備驅(qū)動(dòng)不依賴于任何運(yùn)行的特定進(jìn)程,即使當(dāng)前是為該進(jìn)程工作。與核心的其它部分一樣,設(shè)備驅(qū)動(dòng)使用數(shù)據(jù)結(jié)構(gòu)來(lái)描述它所控制的設(shè)備。這些結(jié)構(gòu)被設(shè)備驅(qū)動(dòng)代碼以靜態(tài)方式分配,但會(huì)增大核心而引起空間的浪費(fèi)。由于設(shè)備驅(qū)動(dòng)屬于核心,所以不能使用虛擬內(nèi)存。多數(shù)設(shè)備驅(qū)動(dòng)使用核心中非頁(yè)面內(nèi)存來(lái)存儲(chǔ)數(shù)據(jù)。Linux為設(shè)備驅(qū)動(dòng)提供了一組核心內(nèi)存分配與回收過(guò)程。核心內(nèi)存以2的冪大小的塊進(jìn)行分配,如512或128字節(jié)。由于設(shè)備驅(qū)動(dòng)的內(nèi)存分配請(qǐng)求可得到的是以塊大小為邊界的內(nèi)存,核心進(jìn)行空閑塊組合更加容易。請(qǐng)求分配核心內(nèi)存時(shí),Linux需要完成許多額外的工作。如果系統(tǒng)中空閑內(nèi)存數(shù)量較少,則可能需要丟棄一些物理頁(yè)面或?qū)⑵鋵懭虢粨Q設(shè)備。一般情況下,Linux將掛起請(qǐng)求者并將此進(jìn)程放置到等待隊(duì)列中,直到系統(tǒng)中有足夠的物理內(nèi)存為止。所以,如果分配核心內(nèi)存的請(qǐng)求不能立刻得到滿足,則此請(qǐng)求可能會(huì)失敗。如果設(shè)備驅(qū)動(dòng)希望在此內(nèi)存中進(jìn)行DMA,那么它必須將此內(nèi)存設(shè)置為DMA使能的。這也是為什么是Linux核心而不是設(shè)備驅(qū)動(dòng)需要了解系統(tǒng)中的DMA使能內(nèi)存的原因。6.3設(shè)備驅(qū)動(dòng)程序
6.3.1設(shè)備驅(qū)動(dòng)程序的概念CPU并不是系統(tǒng)中唯一的智能設(shè)備,每個(gè)物理設(shè)備都擁有自己的控制器。鍵盤、鼠標(biāo)和串行口由一個(gè)高級(jí)I/O芯片統(tǒng)一管理,IDE控制器控制IDE硬盤而SCSI控制器控制SCSI硬盤等等。每個(gè)硬件控制器都有各自的控制和狀態(tài)寄存器(CSR)并且各不相同。這些CSR被用來(lái)啟動(dòng)和停止,初始化設(shè)備及對(duì)設(shè)備進(jìn)行診斷。在Linux中管理硬件設(shè)備控制器的代碼并沒(méi)有放置在每個(gè)應(yīng)用程序中而是由內(nèi)核統(tǒng)一管理。這些處理和管理硬件控制器的軟件就是設(shè)備驅(qū)動(dòng)。Linux核心設(shè)備驅(qū)動(dòng)是一組運(yùn)行在特權(quán)級(jí)上的內(nèi)存駐留底層硬件處理共享庫(kù)。正是它們負(fù)責(zé)管理各個(gè)設(shè)備。設(shè)備驅(qū)動(dòng)的一個(gè)基本特征是設(shè)備處理的抽象概念。所有硬件設(shè)備都被看成普通文件;可以通過(guò)和操縱普通文件相同的標(biāo)準(zhǔn)系統(tǒng)調(diào)用來(lái)打開(kāi)、關(guān)閉、讀取和寫入設(shè)備。系統(tǒng)中每個(gè)設(shè)備都用一種特殊的設(shè)備相關(guān)文件來(lái)表示,例如系統(tǒng)中第一個(gè)IDE硬盤被表示成/dev/hda。塊(磁盤)設(shè)備和字符設(shè)備的設(shè)備相關(guān)文件可以通過(guò)mknod命令來(lái)創(chuàng)建,并使用主從設(shè)備號(hào)來(lái)描述此設(shè)備。網(wǎng)絡(luò)設(shè)備也用設(shè)備相關(guān)文件來(lái)表示,但Linux尋找和初始化網(wǎng)絡(luò)設(shè)備時(shí)才建立這種文件。由同一個(gè)設(shè)備驅(qū)動(dòng)控制的所有設(shè)備具有相同的主設(shè)備號(hào)。從設(shè)備號(hào)則被用來(lái)區(qū)分具有相同主設(shè)備號(hào)且由同一個(gè)設(shè)備驅(qū)動(dòng)控制的不同設(shè)備。例如主IDE硬盤的每個(gè)分區(qū)的從設(shè)備號(hào)都不相同。如/dev/hda2表示主IDE硬盤的主設(shè)備號(hào)為3而從設(shè)備號(hào)為2。Linux通過(guò)使用主從設(shè)備號(hào),將含在系統(tǒng)調(diào)用中的(如將一個(gè)文件系統(tǒng)mount到一個(gè)塊設(shè)備)設(shè)備相關(guān)文件映射到設(shè)備的設(shè)備驅(qū)動(dòng)以及大量系統(tǒng)表格中,如字符設(shè)備表、chrdevs等。在多任務(wù)操作系統(tǒng)中,有時(shí)需要內(nèi)核建立應(yīng)用程序和設(shè)備之間的抽象接口,而不是由應(yīng)用程序直接操作硬件。為此,操作系統(tǒng)一般提供設(shè)備驅(qū)動(dòng)程序來(lái)專門完成對(duì)特定硬件的控制。設(shè)備驅(qū)動(dòng)程序?qū)嶋H是處理或操作硬件控制器的軟件,從本質(zhì)上講,它們是內(nèi)核中具有高特權(quán)級(jí)的、駐留內(nèi)存的、可共享的底層硬件處理例程。在Linux系統(tǒng)中,一個(gè)基本的特點(diǎn)是它抽象了設(shè)備處理。所有對(duì)硬件設(shè)備的操作和通常的文件一樣,利用標(biāo)準(zhǔn)的系統(tǒng)調(diào)用可在設(shè)備上進(jìn)行打開(kāi)、關(guān)閉、讀取或?qū)懭氩僮?。系統(tǒng)中的每個(gè)設(shè)備由“設(shè)備特殊文件”來(lái)代表。例如,/dev/hda代表系統(tǒng)中的第一個(gè)IDE硬盤,而/dev/sda1則代表系統(tǒng)中SCSI硬盤上的第一個(gè)分區(qū)。每個(gè)由相同的設(shè)備驅(qū)動(dòng)程序控制的設(shè)備具有相同的主設(shè)備號(hào),而從設(shè)備號(hào)則用來(lái)區(qū)分同類設(shè)備中不同的設(shè)備。設(shè)備特殊文件的VFS索引節(jié)點(diǎn)中包含設(shè)備號(hào)信息。如果通過(guò)系統(tǒng)調(diào)用訪問(wèn)設(shè)備,則內(nèi)核可通過(guò)該VFS索引節(jié)點(diǎn)中的設(shè)備號(hào)信息調(diào)用適當(dāng)?shù)脑O(shè)備驅(qū)動(dòng)程序。Linux支持字符、塊及網(wǎng)絡(luò)三種設(shè)備硬件。字符設(shè)備指那些無(wú)需緩沖直接讀寫的設(shè)備,如系統(tǒng)的串口設(shè)備/dev/cua0和/dev/cua1。塊設(shè)備則僅能以塊為單位讀寫,典型的塊大小為512或1024字節(jié)。塊設(shè)備的存取是通過(guò)buffercache來(lái)進(jìn)行并且可以進(jìn)行隨機(jī)訪問(wèn),即不管塊位于設(shè)備中何處都可以對(duì)其進(jìn)行讀寫。塊設(shè)備可以通過(guò)其設(shè)備相關(guān)文件進(jìn)行訪問(wèn),但更為平常的訪問(wèn)方法是通過(guò)文件系統(tǒng)。只有塊設(shè)備才能支持可安裝文件系統(tǒng)。網(wǎng)絡(luò)設(shè)備則可以通過(guò)BSD套接字進(jìn)行訪問(wèn)。Linux核心中雖存在許多不同的設(shè)備驅(qū)動(dòng),但它們具有一些共性:(1)核心代碼:設(shè)備驅(qū)動(dòng)是核心的一部分,如同核心中其它代碼一樣,出錯(cuò)將導(dǎo)致系統(tǒng)的嚴(yán)重?fù)p害。編寫得很差的設(shè)備驅(qū)動(dòng)甚至能使系統(tǒng)崩潰并導(dǎo)致文件系統(tǒng)的破壞和數(shù)據(jù)丟失。(2)核心接口:設(shè)備驅(qū)動(dòng)必須為L(zhǎng)inux核心或者其從屬子系統(tǒng)提供一個(gè)標(biāo)準(zhǔn)接口。例如終端驅(qū)動(dòng)為L(zhǎng)inux核心提供了一個(gè)文件I/O接口而SCSI設(shè)備驅(qū)動(dòng)為SCSI子系統(tǒng)提供了一個(gè)SCSI設(shè)備接口,同時(shí)此子系統(tǒng)為核心提供了文件I/O和buffercache接口。(3)核心機(jī)制與服務(wù):設(shè)備驅(qū)動(dòng)可以使用標(biāo)準(zhǔn)的核心服務(wù)如內(nèi)存分配、中斷發(fā)送和等待隊(duì)列等等。(4)動(dòng)態(tài)可加載:多數(shù)Linux設(shè)備驅(qū)動(dòng)可以在核心模塊發(fā)出加載請(qǐng)求時(shí)加載,同時(shí)在不再使用時(shí)卸載。這樣核心能有效地利用系統(tǒng)資源。(5)可配置:Linux設(shè)備驅(qū)動(dòng)可以連接到核心中。當(dāng)核心被編譯時(shí),可配置決定哪些核心被連入核心。(6)動(dòng)態(tài)性:當(dāng)系統(tǒng)啟動(dòng)及設(shè)備驅(qū)動(dòng)初始化時(shí)將查找它所控制的硬件設(shè)備。如果某個(gè)設(shè)備的驅(qū)動(dòng)為一個(gè)空過(guò)程并不會(huì)有什么問(wèn)題。此時(shí)此設(shè)備驅(qū)動(dòng)僅僅是一個(gè)冗余的程序,它除了會(huì)占用少量系統(tǒng)內(nèi)存外不會(huì)對(duì)系統(tǒng)造成什么危害。
6.3.2設(shè)備驅(qū)動(dòng)程序的內(nèi)存分配設(shè)備驅(qū)動(dòng)程序作為內(nèi)核的一部分,不能使用虛擬內(nèi)存,因此也不能依賴于任何一個(gè)進(jìn)程運(yùn)行。和內(nèi)核的其他部分一樣,設(shè)備驅(qū)動(dòng)程序也利用各種數(shù)據(jù)結(jié)構(gòu)跟蹤所控制的設(shè)備。這些數(shù)據(jù)結(jié)構(gòu)可作為驅(qū)動(dòng)程序代碼的一部分而靜態(tài)分配,但這種方法會(huì)導(dǎo)致內(nèi)核變大,甚至浪費(fèi)內(nèi)存。為此,大部分驅(qū)動(dòng)程序利用內(nèi)核提供的服務(wù)函數(shù)動(dòng)態(tài)分配和釋放非分頁(yè)的系統(tǒng)內(nèi)存。內(nèi)核以2的冪為大小分配內(nèi)存,例如128字節(jié)或512字節(jié)。驅(qū)動(dòng)程序要求的字節(jié)數(shù)被調(diào)整到下一個(gè)頁(yè)塊邊界上,這樣,釋放的物理內(nèi)存可以重新組合成大的頁(yè)塊。當(dāng)系統(tǒng)內(nèi)存較少時(shí),內(nèi)核會(huì)通過(guò)交換操作將某些物理內(nèi)存頁(yè)交換到交換空間中,為此,內(nèi)核會(huì)將請(qǐng)求分配的驅(qū)動(dòng)程序暫時(shí)掛起,直到有足夠的內(nèi)存時(shí)才分配內(nèi)存并返回成功值。但是,有時(shí)驅(qū)動(dòng)程序不允許內(nèi)存分配上的延遲,因此,可請(qǐng)求內(nèi)核在這種情況下返回失敗值。另外,當(dāng)驅(qū)動(dòng)程序需要分配用來(lái)進(jìn)行DMA操作的內(nèi)存時(shí),須指定該內(nèi)存應(yīng)當(dāng)是可進(jìn)行DMA操作的內(nèi)存(處于低16MB地址空間中)以便能正確分配內(nèi)存。6.3.3設(shè)備驅(qū)動(dòng)程序和內(nèi)核的接口每種類型的驅(qū)動(dòng)程序,不管它們是字符設(shè)備、塊設(shè)備還是網(wǎng)絡(luò)設(shè)備,均為內(nèi)核提供相同的調(diào)用接口。于是,內(nèi)核可以以相同的方式處理不同的設(shè)備。例如,內(nèi)核可通過(guò)相同的函數(shù)調(diào)用讓SCSI和IDE硬盤完成相同的工作。表6.2還說(shuō)明了Linux設(shè)備驅(qū)動(dòng)程序具備的其他一些特點(diǎn)。為了實(shí)現(xiàn)這些特點(diǎn),Linux為每種不同類型的設(shè)備驅(qū)動(dòng)程序維護(hù)相應(yīng)的數(shù)據(jù)結(jié)構(gòu),以便定義統(tǒng)一的接口并實(shí)現(xiàn)驅(qū)動(dòng)程序的可裝載性、動(dòng)態(tài)性等。1.字符設(shè)備字符設(shè)備是Linux設(shè)備中最簡(jiǎn)單的一種。應(yīng)用程序可以和存取文件相同的系統(tǒng)調(diào)用來(lái)打開(kāi)、讀寫及關(guān)閉它。即使此設(shè)備是將Linux系統(tǒng)連接到網(wǎng)絡(luò)中的PPP后臺(tái)進(jìn)程的modem也是如此。字符設(shè)備初始化時(shí),它的設(shè)備驅(qū)動(dòng)通過(guò)在device_struct結(jié)構(gòu)的chrdevs數(shù)組中添加一個(gè)入口來(lái)將其注冊(cè)到Linux核心上。設(shè)備的主設(shè)備標(biāo)志符用來(lái)對(duì)此數(shù)組進(jìn)行索引(如對(duì)tty設(shè)備的索引4)。設(shè)備的主設(shè)備標(biāo)志符是固定的。對(duì)字符設(shè)備來(lái)說(shuō),Linux內(nèi)核維護(hù)一個(gè)chrdevs結(jié)構(gòu)體數(shù)組,/proc/devices中字符設(shè)備的內(nèi)容來(lái)自chrdevs數(shù)組。該數(shù)組的元素實(shí)際是device_struct結(jié)構(gòu)(圖6.5)。主設(shè)備號(hào)用作訪問(wèn)chrdevs數(shù)組的索引,例如,tty設(shè)備的主設(shè)備號(hào)為4。每個(gè)device_struct結(jié)構(gòu)包含兩部分信息。name是設(shè)備驅(qū)動(dòng)程序注冊(cè)的設(shè)備名稱;另一部分包含設(shè)備的標(biāo)準(zhǔn)操作例程集,其中的操作例程由設(shè)備驅(qū)動(dòng)程序定義,并分別完成指定的設(shè)備操作。Namefops
chrdevs文件操作集
lseekreadwritereaddirselectiocltmmapopenreleasefsynfasynccheck_me-dia_changerevalidate圖6.5Linux內(nèi)核維護(hù)的字符設(shè)備數(shù)據(jù)結(jié)構(gòu)
對(duì)字符設(shè)備來(lái)說(shuō),Linux內(nèi)核維護(hù)一個(gè)chrdevs結(jié)構(gòu)體數(shù)組,/proc/devices中字符設(shè)備的內(nèi)容來(lái)自chrdevs數(shù)組。該數(shù)組的元素實(shí)際是device_struct結(jié)構(gòu)(圖6.5)。主設(shè)備號(hào)用作訪問(wèn)chrdevs數(shù)組的索引,例如,tty設(shè)備的主設(shè)備號(hào)為4。每個(gè)device_struct結(jié)構(gòu)包含兩部分信息。name是設(shè)備驅(qū)動(dòng)程序注冊(cè)的設(shè)備名稱;另一部分包含設(shè)備的標(biāo)準(zhǔn)操作例程集,其中的操作例程由設(shè)備驅(qū)動(dòng)程序定義,并分別完成指定的設(shè)備操作。chrdevs數(shù)組每個(gè)入口中的device_struct數(shù)據(jù)結(jié)構(gòu)包含兩個(gè)元素;一個(gè)指向已注冊(cè)的設(shè)備驅(qū)動(dòng)名稱,另一個(gè)則是指向一組文件操作指針。它們是位于此字符設(shè)備驅(qū)動(dòng)內(nèi)部的文件操作例程的地址指針,用來(lái)處理相關(guān)的文件操作如打開(kāi)、讀寫與關(guān)閉。當(dāng)打開(kāi)代表字符設(shè)備的字符特殊文件時(shí)(如/dev/cua0),核心必須作好準(zhǔn)備以便調(diào)用相應(yīng)字符設(shè)備驅(qū)動(dòng)的文件操作例程。與普通的目錄和文件一樣,每個(gè)字符特殊文件用一個(gè)虛擬文件系統(tǒng)(VirtualFileSystem,VFS)的節(jié)點(diǎn)(inode)表示。每個(gè)字符特殊文件使用的VFSinode和所有設(shè)備特殊文件一樣,包含著設(shè)備的主從標(biāo)志符。這個(gè)VFSinode由底層的文件系統(tǒng)來(lái)建立(如EXT2),其信息來(lái)源于設(shè)備相關(guān)文件名稱所在文件系統(tǒng)。每個(gè)VFSinode和一組文件操作相關(guān)聯(lián),它們根據(jù)inode代表的文件系統(tǒng)對(duì)像變化而不同。當(dāng)創(chuàng)建一個(gè)代表字符相關(guān)文件的VFSinode時(shí),其文件操作被設(shè)置為默認(rèn)的字符設(shè)備操作。字符設(shè)備只有一個(gè)文件操作:打開(kāi)文件操作。當(dāng)應(yīng)用打開(kāi)字符特殊文件時(shí),通用文件打開(kāi)操作使用設(shè)備的主標(biāo)志符來(lái)索引此chrdevs數(shù)組,以便得到那些文件操作函數(shù)指針。同時(shí)建立起描述此字符特殊文件的file結(jié)構(gòu),使其文件操作指針指向此設(shè)備驅(qū)動(dòng)中的文件操作指針集合。這樣所有應(yīng)用對(duì)它進(jìn)行的文件操作都被映射到此字符設(shè)備的文件操作集合上。當(dāng)代表某個(gè)字符設(shè)備的設(shè)備特殊文件被打開(kāi)時(shí),內(nèi)核負(fù)責(zé)進(jìn)行一些設(shè)置工作,以便能夠調(diào)用正確的設(shè)備操作例程。和其他普通的文件或目錄一樣,設(shè)備文件也由VFS索引節(jié)點(diǎn)代表。VFS索引節(jié)點(diǎn)中包含設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào),每個(gè)VFS節(jié)點(diǎn)和一組文件操作函數(shù)的指針關(guān)聯(lián)。系統(tǒng)在建立一個(gè)代表字符設(shè)備文件的VFS索引節(jié)點(diǎn)時(shí),該VFS節(jié)點(diǎn)的文件操作函數(shù)指針被設(shè)置為默認(rèn)的字符設(shè)備操作函數(shù)指針。這時(shí)實(shí)際只有一個(gè)文件操作函數(shù)被定義,即文件的打開(kāi)操作。應(yīng)用程序打開(kāi)某個(gè)字符特殊文件時(shí),該打開(kāi)操作將使用VFS節(jié)點(diǎn)中的主設(shè)備號(hào)在chrdevs數(shù)組中檢索相應(yīng)的設(shè)備操作函數(shù)地址,同時(shí)在進(jìn)程的文件數(shù)據(jù)結(jié)構(gòu)中建立一個(gè)file結(jié)構(gòu),該結(jié)構(gòu)的文件操作函數(shù)指針指向設(shè)備驅(qū)動(dòng)程序定義的設(shè)備操作函數(shù)指針。此后,應(yīng)用程序在該設(shè)備特殊文件上進(jìn)行的讀取、寫入等操作就映射到了特定的字符設(shè)備操作。2.塊設(shè)備和字符設(shè)備類似,Linux內(nèi)核利用blkdevs數(shù)組記錄所有的塊設(shè)備,每個(gè)數(shù)組元素也是一個(gè)device_struct結(jié)構(gòu)體,主設(shè)備也當(dāng)作數(shù)組索引使用。在device_struct結(jié)構(gòu)體中,塊設(shè)備驅(qū)動(dòng)程序?yàn)橥ǔ5奈募僮魈峁┙涌凇3酥?,塊設(shè)備在內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)比字符設(shè)備要復(fù)雜一些,主要是因?yàn)閮?nèi)核為塊設(shè)備提供緩沖區(qū)高速緩存,實(shí)際的讀寫操作由緩沖區(qū)緩存協(xié)調(diào)調(diào)用,因此,還需要驅(qū)動(dòng)程序?yàn)榫彌_區(qū)緩存提供接口。為此,內(nèi)核利用blk_dev數(shù)組(圖6.6)定義這些接口。訪問(wèn)該數(shù)組的索引仍然是主設(shè)備號(hào),每個(gè)塊設(shè)備驅(qū)動(dòng)程序在blk_dev數(shù)組的相應(yīng)位置填充dlk_dev_struct結(jié)構(gòu)。blk_dev_struct結(jié)構(gòu)包含一個(gè)處理請(qǐng)求的函數(shù)地址,以及一個(gè)指向request結(jié)構(gòu)體的指針。實(shí)際上,多個(gè)request結(jié)構(gòu)形成了一個(gè)鏈表,而每個(gè)request結(jié)構(gòu)代表由緩沖區(qū)高速緩存請(qǐng)求設(shè)備讀取或?qū)懭氲恼?qǐng)求信息。每當(dāng)緩沖區(qū)緩存要從注冊(cè)設(shè)備中讀取數(shù)據(jù)或向設(shè)備寫入數(shù)據(jù)時(shí),緩沖區(qū)緩存就會(huì)在blk_dev_struct中添加一個(gè)request結(jié)構(gòu)。從圖6.6可看出,每個(gè)request結(jié)構(gòu)中包含一個(gè)指向buffer_head結(jié)構(gòu)的指針,每個(gè)buffer_head結(jié)構(gòu)定義要讀取或?qū)懭氲臄?shù)據(jù)塊。進(jìn)行讀寫操作時(shí),buffer_head結(jié)構(gòu)由緩沖區(qū)緩存鎖定,這時(shí),等待該緩沖區(qū)操作結(jié)束的進(jìn)程被阻塞。當(dāng)請(qǐng)求被添加到空的請(qǐng)求隊(duì)列時(shí),驅(qū)動(dòng)程序的請(qǐng)求函數(shù)(request_fn)被調(diào)用,由該函數(shù)完成請(qǐng)求隊(duì)列的處理。圖6.6塊設(shè)備的緩沖當(dāng)驅(qū)動(dòng)程序完成請(qǐng)求處理之后,它從request結(jié)構(gòu)中移去buffer_head結(jié)構(gòu)并標(biāo)志該結(jié)構(gòu)包含新數(shù)據(jù),然后解鎖buffer_head結(jié)構(gòu)。對(duì)buffer_head結(jié)構(gòu)的解鎖將喚醒所有正在等待該操作完成的進(jìn)程。request結(jié)構(gòu)并不是動(dòng)態(tài)分配的,每次需要新的request時(shí),該結(jié)構(gòu)從一個(gè)大的靜態(tài)all_request鏈表中取得,對(duì)應(yīng)的請(qǐng)求被處理之后,該結(jié)構(gòu)標(biāo)志為空閑的request結(jié)構(gòu)。和普通文件操作接口一樣,每個(gè)塊設(shè)備驅(qū)動(dòng)必須為buffercache提供接口。每個(gè)塊設(shè)備驅(qū)動(dòng)將填充其在blk_dev數(shù)組中的blk_dev_struct結(jié)構(gòu)入口。數(shù)組的索引值還是此設(shè)備的主設(shè)備號(hào)。這個(gè)blk_dev_struct結(jié)構(gòu)包含請(qǐng)求過(guò)程的地址以及指向請(qǐng)求數(shù)據(jù)結(jié)構(gòu)鏈表的指針,每個(gè)代表一個(gè)從buffercache中來(lái)讓設(shè)備進(jìn)行數(shù)據(jù)讀寫的請(qǐng)求。
每當(dāng)buffercache希望從一個(gè)已注冊(cè)設(shè)備中讀寫數(shù)據(jù)塊時(shí),它會(huì)將request結(jié)構(gòu)添加到其blk_dev_struct中。圖6.6表示每個(gè)請(qǐng)求有指向一個(gè)或多個(gè)buffer_hear結(jié)構(gòu)的指針,每個(gè)請(qǐng)求讀寫一塊數(shù)據(jù)。如buffercache對(duì)buffer_head結(jié)構(gòu)上鎖,則進(jìn)程會(huì)等待到對(duì)此緩沖的塊操作完成。每個(gè)request結(jié)構(gòu)都從靜態(tài)鏈表all_requests中分配。如果此請(qǐng)求被加入到空請(qǐng)求鏈表中,則將調(diào)用驅(qū)動(dòng)請(qǐng)求函數(shù)以啟動(dòng)此請(qǐng)求隊(duì)列的處理,否則該設(shè)備驅(qū)動(dòng)將簡(jiǎn)單地處理請(qǐng)求鏈表上的request。一旦設(shè)備驅(qū)動(dòng)完成了請(qǐng)求則它必須將每個(gè)buffer_heard結(jié)構(gòu)從request結(jié)構(gòu)中清除,將它們標(biāo)記成已更新?tīng)顟B(tài)并解鎖之。對(duì)buffer_head的解鎖將喚醒所有等待此塊操作完成的睡眠進(jìn)程。如解析文件名稱時(shí),EXT2文件系統(tǒng)必須從包含此文件系統(tǒng)的設(shè)備中讀取包含下個(gè)EXT2目錄入口的數(shù)據(jù)塊。在設(shè)備驅(qū)動(dòng)被喚醒后,在buffer_head上睡眠的進(jìn)程將包含此目錄入口。request數(shù)據(jù)結(jié)構(gòu)被標(biāo)記成空閑以便被其它塊請(qǐng)求使用。塊設(shè)備也支持以文件方式訪問(wèn)。系統(tǒng)對(duì)塊設(shè)備特殊文件提供了非常類似于字符特殊文件的文件操作機(jī)制。Linux在blkdevs數(shù)組中維護(hù)所有已注冊(cè)的塊設(shè)備。像chrdevs數(shù)組一樣,blkdevs也使用設(shè)備的主設(shè)備號(hào)進(jìn)行索引。其入口也是device_struct結(jié)構(gòu)。和字符設(shè)備不同的是,塊設(shè)備分成不同的類別,例如SCSI設(shè)備和IDE設(shè)備。它們將以各自類別登記到Linux核心中并為核心提供文件操作功能。某類塊設(shè)備的設(shè)備驅(qū)動(dòng)為此類型設(shè)備提供了類別相關(guān)的接口。如SCSI設(shè)備驅(qū)動(dòng)必須為SCSI子系統(tǒng)提供接口,以便SCSI子系統(tǒng)能用它來(lái)為核心提供對(duì)此設(shè)備的文件操作。3.網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備,即Linux的網(wǎng)絡(luò)子系統(tǒng),是一個(gè)發(fā)送與接收數(shù)據(jù)包的實(shí)體。它一般是一個(gè)像以太網(wǎng)卡的物理設(shè)備。有些網(wǎng)絡(luò)設(shè)備如loopback設(shè)備僅僅是一個(gè)用來(lái)向自身發(fā)送數(shù)據(jù)的軟件。每個(gè)網(wǎng)絡(luò)設(shè)備都用一個(gè)device結(jié)構(gòu)來(lái)表示。網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)在核心啟動(dòng)初始化網(wǎng)絡(luò)時(shí)將這些受控設(shè)備登記到Linux中。device數(shù)據(jù)結(jié)構(gòu)中包含有有關(guān)設(shè)備的信息以及用來(lái)支持各種網(wǎng)絡(luò)協(xié)議的函數(shù)地址指針。這些函數(shù)主要用來(lái)使用網(wǎng)絡(luò)設(shè)備傳輸數(shù)據(jù)。設(shè)備使用標(biāo)準(zhǔn)網(wǎng)絡(luò)支持機(jī)制來(lái)將接收到的數(shù)據(jù)傳遞到適當(dāng)?shù)膮f(xié)議層。所有傳輸與接收到的網(wǎng)絡(luò)數(shù)據(jù)用一個(gè)sk_buff結(jié)構(gòu)表示,這些靈活的數(shù)據(jù)結(jié)構(gòu)使得網(wǎng)絡(luò)協(xié)議頭可以更容易的添加與刪除。網(wǎng)絡(luò)協(xié)議層如何使用網(wǎng)絡(luò)設(shè)備以及如何使用sk_buff來(lái)交換數(shù)據(jù)將在網(wǎng)絡(luò)一章中詳細(xì)描述。本章只討論device數(shù)據(jù)結(jié)構(gòu)及如何發(fā)現(xiàn)與初始化網(wǎng)絡(luò)。(1)網(wǎng)絡(luò)設(shè)備的數(shù)據(jù)結(jié)構(gòu)device網(wǎng)絡(luò)設(shè)備的數(shù)據(jù)結(jié)構(gòu)device中包含以下有關(guān)網(wǎng)絡(luò)設(shè)備的信息:Name:與使用mknod命令創(chuàng)建的塊設(shè)備特殊文件與字符設(shè)備特殊文件不同,網(wǎng)絡(luò)設(shè)備特殊文件僅在于系統(tǒng)網(wǎng)絡(luò)設(shè)備發(fā)現(xiàn)與初始化時(shí)建立。它們使用標(biāo)準(zhǔn)的命名方法,每個(gè)名字代表一種類型的設(shè)備。多個(gè)相同類型設(shè)備將從0開(kāi)始記數(shù)。這樣以太網(wǎng)設(shè)備被命名為/dev/eth0,/dev/eth1,/dev/eth2等等。一些常見(jiàn)的網(wǎng)絡(luò)設(shè)備如下:
BusInformation:這些信息被設(shè)備驅(qū)動(dòng)用來(lái)控制設(shè)備。irq號(hào)表示設(shè)備使用的中斷號(hào)。baseaddress指任何設(shè)備在I/O內(nèi)存中的控制與狀態(tài)寄存器地址。DMA通道指此網(wǎng)絡(luò)設(shè)備使用的DMA通道號(hào)。所有這些信息在設(shè)備初始化時(shí)設(shè)置。/dev/ethN以太網(wǎng)設(shè)備
/dev/slN
SLIP設(shè)備
/dev/pppNPPP設(shè)備
/dev/lo
Loopback設(shè)備InterfaceFlags:它們描述了網(wǎng)絡(luò)設(shè)備的屬性與功能:
IFF_UP
接口已經(jīng)建立并運(yùn)行
IFF_BROADCAST
設(shè)備中的廣播地址有效IFF_DEBUG
設(shè)備調(diào)試被使能
IFF_LOOPBACK一個(gè)loopback設(shè)備
IFF_POINTTOPOINT點(diǎn)到點(diǎn)連接(SLIP和PPP)
IFF_NOTRAILERS無(wú)網(wǎng)絡(luò)追蹤者
IFF_RUNNING資源已被分配IFF_NOARP不支持ARP協(xié)議
IFF_PROMISC設(shè)備處于混亂的接收模式,無(wú)論包地址怎樣它都將接收
IFF_ALLMULTI接收所有的IP多播幀IFF_MULTICAST可以接收IP多播幀
ProtocolInformation:每個(gè)設(shè)備描述它可以被網(wǎng)絡(luò)協(xié)議層如何使用:mtu:指不包括任何鏈路層頭在內(nèi)的,網(wǎng)絡(luò)可傳送的最大包大小。這個(gè)值被協(xié)議層用來(lái)選擇適當(dāng)大小的包進(jìn)行發(fā)送。Family:這個(gè)family域表示設(shè)備支持的協(xié)議族。所有Linux網(wǎng)絡(luò)設(shè)備的族是AF_INET,互聯(lián)網(wǎng)地址族。Type:這個(gè)硬件接口類型描述網(wǎng)絡(luò)設(shè)備連接的介質(zhì)類型。Linux網(wǎng)絡(luò)設(shè)備可以支持多種不同類型的介質(zhì)。包括以太網(wǎng)、X.25,令牌環(huán),Slip,PPP和AppleLocaltalk。Addresses:結(jié)構(gòu)中包含大量網(wǎng)絡(luò)設(shè)備相關(guān)的地址,包括IP地址。PacketQueue:指網(wǎng)絡(luò)設(shè)備上等待傳輸?shù)膕k_buff包隊(duì)列。SupportFunctions:每個(gè)設(shè)備支持一組標(biāo)準(zhǔn)的例程,它們被協(xié)議層作為設(shè)備鏈路層的接口而調(diào)用。如傳輸建立和幀傳輸例程以及添加標(biāo)準(zhǔn)幀頭以及收集統(tǒng)計(jì)數(shù)據(jù)的例程。這些統(tǒng)計(jì)數(shù)據(jù)可以使用ifconfig命令來(lái)觀察。(2).
網(wǎng)絡(luò)設(shè)備的初始化網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)可以像其它Linux設(shè)備驅(qū)動(dòng)一樣建立到Linux核心中。每個(gè)潛在的網(wǎng)絡(luò)設(shè)備由一個(gè)被dev_base鏈表指針指向的網(wǎng)絡(luò)設(shè)備鏈表內(nèi)部的device結(jié)構(gòu)表示。當(dāng)網(wǎng)絡(luò)層需要某個(gè)特定工作執(zhí)行時(shí)。它將調(diào)用大量網(wǎng)絡(luò)服務(wù)例程中的一個(gè),這些例程的地址被保存在device結(jié)構(gòu)內(nèi)部。初始化時(shí)每個(gè)device結(jié)構(gòu)僅包含一個(gè)初始化或者檢測(cè)例程的地址。對(duì)于網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)有兩個(gè)問(wèn)題需要解決。首先,不是每個(gè)連接到核心中的網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)都有設(shè)備要控制。其次雖然底層的設(shè)備驅(qū)動(dòng)迥然不同,但系統(tǒng)中的以太網(wǎng)設(shè)備總是命名為/dev/eth0和/dev/eth1?;煜W(wǎng)絡(luò)設(shè)備這個(gè)問(wèn)題很容易解決。當(dāng)每個(gè)網(wǎng)絡(luò)設(shè)備的初始化例程被調(diào)用時(shí),將得到一個(gè)指示是否存在當(dāng)前控制器實(shí)例的狀態(tài)信息。如果驅(qū)動(dòng)找不到任何設(shè)備,它那個(gè)由dev_base指向的device鏈表將被刪除。如果驅(qū)動(dòng)找到了設(shè)備,則它將用設(shè)備相關(guān)信息以及網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)中支撐函數(shù)的地址指針來(lái)填充此device數(shù)據(jù)結(jié)構(gòu)。第二個(gè)問(wèn)題是要為以太網(wǎng)設(shè)備動(dòng)態(tài)分配設(shè)備特殊文件的標(biāo)準(zhǔn)名/dev/ethN找到解決方法。在設(shè)備鏈表中有8個(gè)標(biāo)準(zhǔn)入口;從eth0到eth7。它們使用相同的初始化例程,此初始化過(guò)程將依次嘗試這些被建立到核心中的以太網(wǎng)設(shè)備驅(qū)動(dòng),直到找到一個(gè)設(shè)備。當(dāng)驅(qū)動(dòng)找到該以太網(wǎng)設(shè)備時(shí),它將填充對(duì)應(yīng)的ethN設(shè)備結(jié)構(gòu)。同時(shí),此網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)初始化其控制的物理硬件,并找出使用的IRQ號(hào)以及DMA通道等信息。如果驅(qū)動(dòng)找到了此網(wǎng)絡(luò)設(shè)備的多個(gè)實(shí)例,它將建立多個(gè)/dev/ethNdevice數(shù)據(jù)結(jié)構(gòu)。一旦所有8個(gè)標(biāo)準(zhǔn)/dev/ethN被分配完畢,則不再檢測(cè)其它的以太網(wǎng)設(shè)備。6.4
編寫Linux下的設(shè)備驅(qū)動(dòng)程序
Linux核心與設(shè)備驅(qū)動(dòng)之間必須有一個(gè)以標(biāo)準(zhǔn)方式進(jìn)行互操作的接口。每一類設(shè)備驅(qū)動(dòng):字符設(shè)備、塊設(shè)備及網(wǎng)絡(luò)設(shè)備都提供了通用接口,以便在需要時(shí)為核心提供服務(wù)。這種通用接口使得核心可以以相同的方式來(lái)對(duì)待不同的設(shè)備及設(shè)備驅(qū)動(dòng)。如SCSI和IDE硬盤的區(qū)別很大,但Linux對(duì)它們使用相同的接口。Linux動(dòng)態(tài)性很強(qiáng)。每次Linux核心啟動(dòng)時(shí)如遇到不同的物理設(shè)備將需要不同的物理設(shè)備驅(qū)動(dòng)。Linux允許通過(guò)配置腳本在核心重建時(shí)將設(shè)備驅(qū)動(dòng)包含在內(nèi)。設(shè)備驅(qū)動(dòng)在啟動(dòng)初始化時(shí)可能會(huì)發(fā)現(xiàn)系統(tǒng)中根本沒(méi)有任何硬件需要控制。其它設(shè)備驅(qū)動(dòng)可以在必要時(shí)作為核心模塊動(dòng)態(tài)加載到。為了處理設(shè)備驅(qū)動(dòng)的動(dòng)態(tài)屬性,設(shè)備驅(qū)動(dòng)在初始化時(shí)將其注冊(cè)到核心中去。Linux維護(hù)著已注冊(cè)設(shè)備驅(qū)動(dòng)表作為和設(shè)備驅(qū)動(dòng)的接口。這些表中包含支持此類設(shè)備例程的指針和相關(guān)信息。6.4.1Unix下設(shè)備驅(qū)動(dòng)程序的基本結(jié)構(gòu)對(duì)用戶程序來(lái)說(shuō),Unix系統(tǒng)的設(shè)備驅(qū)動(dòng)程序隱藏了設(shè)備的具體細(xì)節(jié),對(duì)各種不同設(shè)備提供了一致的接口,一般來(lái)說(shuō)是把設(shè)備映射為一個(gè)特殊的設(shè)備文件,用戶程序可以像對(duì)其它文件一樣對(duì)此設(shè)備文件進(jìn)行操作。Unix對(duì)硬件設(shè)備支持兩個(gè)標(biāo)準(zhǔn)接口:塊特別設(shè)備文件和字符特別設(shè)備文件,通過(guò)塊(字符)特別設(shè)備文件存取的設(shè)備稱為塊(字符)設(shè)備或具有塊(字符)設(shè)備接口。塊設(shè)備接口僅支持面向塊的I/O操作,所有I/O操作都通過(guò)在內(nèi)核地址空間中的I/O緩沖區(qū)進(jìn)行,它可以支持幾乎任意長(zhǎng)度和任意位置上的I/O請(qǐng)求,即提供隨機(jī)存取的功能。字符設(shè)備接口支持面向字符的I/O操作,它不經(jīng)過(guò)系統(tǒng)的快速緩存,所以它們負(fù)責(zé)管理自己的緩沖區(qū)結(jié)構(gòu)。字符設(shè)備接口只支持順序存取的功能,一般不能進(jìn)行任意長(zhǎng)度的I/O請(qǐng)求,而是限制I/O請(qǐng)求的長(zhǎng)度必須是設(shè)備要求的基本塊長(zhǎng)的倍數(shù)。顯然,本程序所驅(qū)動(dòng)的串行卡只能提供順序存取的功能,屬于是字符設(shè)備,因此后面的討論在兩種設(shè)備有所區(qū)別時(shí)都只涉及字符型設(shè)備接口。設(shè)備由一個(gè)主設(shè)備號(hào)和一個(gè)次設(shè)備號(hào)標(biāo)識(shí)。主設(shè)備號(hào)唯一標(biāo)識(shí)了設(shè)備類型,即設(shè)備驅(qū)動(dòng)程序類型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項(xiàng)的索引。次設(shè)備號(hào)僅由設(shè)備驅(qū)動(dòng)程序解釋,一般用于識(shí)別在若干可能的硬件設(shè)備中,I/O請(qǐng)求所涉及到的那個(gè)設(shè)備。設(shè)備驅(qū)動(dòng)程序可以分為三個(gè)主要組成部分:(1)
自動(dòng)配置和初始化子程序,負(fù)責(zé)檢測(cè)所要驅(qū)動(dòng)的硬件設(shè)備是否存在和是否能正常工作。如果該設(shè)備正常,則對(duì)這個(gè)設(shè)備及其相關(guān)的、設(shè)備驅(qū)動(dòng)程序需要的軟件狀態(tài)進(jìn)行初始化。這部分驅(qū)動(dòng)程序僅在初始化的時(shí)候被調(diào)用一次。(2)
服務(wù)于I/O請(qǐng)求的子程序,又稱為驅(qū)動(dòng)程序的上半部分。調(diào)用這部分是由于系統(tǒng)調(diào)用的結(jié)果。這部分程序在執(zhí)行的時(shí)候,系統(tǒng)仍認(rèn)為是和進(jìn)行調(diào)用的進(jìn)程屬于同一個(gè)進(jìn)程,只是由用戶態(tài)變成了核心態(tài),具有進(jìn)行此系統(tǒng)調(diào)用的用戶程序的運(yùn)行環(huán)境,因此可以在其中調(diào)用sleep()等與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù)。(3)
中斷服務(wù)子程序,又稱為驅(qū)動(dòng)程序的下半部分。在Unix系統(tǒng)中,并不是直接從中斷向量表中調(diào)用設(shè)備驅(qū)動(dòng)程序的中斷服務(wù)子程序,而是由Unix系統(tǒng)來(lái)接收硬件中斷,再由系統(tǒng)調(diào)用中斷服務(wù)子程序。中斷可以產(chǎn)生在任何一個(gè)進(jìn)程運(yùn)行的時(shí)候,因此在中斷服務(wù)程序被調(diào)用的時(shí)候,不能依賴于任何進(jìn)程的狀態(tài),也就不能調(diào)用任何與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù)。因?yàn)樵O(shè)備驅(qū)動(dòng)程序一般支持同一類型的若干設(shè)備,所以一般在系統(tǒng)調(diào)用中斷服務(wù)子程序的時(shí)候,都帶有一個(gè)或多個(gè)參數(shù),以唯一標(biāo)識(shí)請(qǐng)求服務(wù)的設(shè)備。在系統(tǒng)內(nèi)部,I/O設(shè)備的存取通過(guò)一組固定的入口點(diǎn)來(lái)進(jìn)行,這組入口點(diǎn)是由每個(gè)設(shè)備的設(shè)備驅(qū)動(dòng)程序提供的。一般來(lái)說(shuō),字符型設(shè)備驅(qū)動(dòng)程序能夠提供如下幾個(gè)入口點(diǎn):(1)
open入口點(diǎn)。打開(kāi)設(shè)備準(zhǔn)備I/O操作。對(duì)字符特別設(shè)備文件進(jìn)行打開(kāi)操作,都會(huì)調(diào)用設(shè)備的open入口點(diǎn)。open子程序必須對(duì)將要進(jìn)行的I/O操作做好必要的準(zhǔn)備工作,如清除緩沖區(qū)等。如果設(shè)備是獨(dú)占的,即同一
時(shí)刻只能有一個(gè)程序訪問(wèn)此設(shè)備,則open子程序必須設(shè)置一些標(biāo)志以表示設(shè)備處于忙狀態(tài)。(2)
close入口點(diǎn)。關(guān)閉一個(gè)設(shè)備。當(dāng)最后一次使用設(shè)備終結(jié)后,調(diào)用close子程序。獨(dú)占設(shè)備必須標(biāo)記設(shè)備可再次使用。(3)
read入口點(diǎn)。從設(shè)備上讀數(shù)據(jù)。對(duì)于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對(duì)字符特別設(shè)備文件進(jìn)行讀操作將調(diào)用read子程序。(4)
write入口點(diǎn)。往設(shè)備上寫數(shù)據(jù)。對(duì)于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對(duì)字符特別設(shè)備文件進(jìn)行寫操作將調(diào)用write子程序。(5)
ioctl入口點(diǎn)。執(zhí)行讀、寫之外的操作。(6)
select入口點(diǎn)。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。(2)
close入口點(diǎn)。關(guān)閉一個(gè)設(shè)備。當(dāng)最后一次使用設(shè)備終結(jié)后,調(diào)用close子程序。獨(dú)占設(shè)備必須標(biāo)記設(shè)備可再次使用。(3)
read入口點(diǎn)。從設(shè)備上讀數(shù)據(jù)。對(duì)于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對(duì)字符特別設(shè)備文件進(jìn)行讀操作將調(diào)用read子程序。(4)
write入口點(diǎn)。往設(shè)備上寫數(shù)據(jù)。對(duì)于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對(duì)字符特別設(shè)備文件進(jìn)行寫操作將調(diào)用write子程序。(5)
ioctl入口點(diǎn)。執(zhí)行讀、寫之外的操作。(6)
select入口點(diǎn)。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備特別文件相關(guān)的文件描述符時(shí)使用select入口點(diǎn)。如果設(shè)備驅(qū)動(dòng)程序沒(méi)有提供上述入口點(diǎn)中的某一個(gè),系統(tǒng)會(huì)用默認(rèn)的子程序來(lái)代替。對(duì)于不同的系統(tǒng),也還有一些其它的入口點(diǎn)。6.4.2Linux系統(tǒng)下的設(shè)備驅(qū)動(dòng)程序Linux系統(tǒng)里,設(shè)備驅(qū)動(dòng)程序所提供的這組入口點(diǎn)由一個(gè)結(jié)構(gòu)來(lái)向系統(tǒng)進(jìn)行說(shuō)明,此結(jié)構(gòu)定義為:#include
<linux/fs.h>struct
file_operations
{
int
(*lseek)(struct
inode
*inode,struct
file
*filp,
off_t
off,int
pos);
int
(*read)(struct
inode
*inode,struct
file
*filp,
char
*buf,
int
count);
int
(*write)(struct
inode
*inode,struct
file
*filp,
char
*buf,int
count);
int
(*readdir)(struct
inode
*inode,struct
file
*filp,
struct
dirent
*dirent,int
count);
int
(*select)(struct
inode
*inode,struct
file
*filp,
int
sel_type,select_table
*wait);
int
(*ioctl)
(struct
inode
*inode,struct
file
*filp,
unsigned
int
cmd,unsigned
int
arg);
int
(*mmap)
(void);
int
(*open)
(struct
inode
*inode,
struct
file
*filp);
void
(*release)
(struct
inode
*inode,
struct
file
*filp);
int
(*fsync)
(struct
inode
*inode,
struct
file
*filp);};其中,struct
inode提供了關(guān)于特別設(shè)備文件/dev/driver(假設(shè)此設(shè)備名為driver)的信息,它的定義為:#include
<linux/fs.h>struct
inode
{
dev_t
i_dev;
unsigned
long
i_ino;
//
Inode
number
umode_t
i_mode;
//
Mode
of
the
file
nlink_t
i_nlink;
uid_t
i_uid;
gid_t
i_gid;
dev_t
i_rdev;
//
Device
major
and
minor
numbers
off_t
i_size;
time_t
i_atime;
time_t
i_mtime;
time_t
i_ctime;
unsigned
long
i_blksize;
unsigned
long
i_blocks;
struct
inode_operations
*
i_op;
struct
super_block
*
i_sb;
struct
wait_queue
*
i_wait;
struct
file_lock
*
i_flock;
struct
vm_area_struct
*
i_mmap;
struct
inode
*
i_next,
*
i_prev;
struct
inode
*
i_hash_next,
*
i_hash_prev;
struct
inode
*
i_bound_to,
*
i_bound_by;
unsigned
short
i_count;
unsigned
short
i_flags;
//
Mount
flags
(see
fs.h)
unsigned
char
i_lock;
unsigned
char
i_dirt;
unsigned
char
i_pipe;
unsigned
char
i_mount;
unsigned
char
i_seek;
unsigned
char
i_update;
union
{
struct
pipe_inode_info
pipe_i;
struct
minix_inode_info
minix_i;
struct
ext_inode_info
ext_i;
struct
msdos_inode_info
msdos_i;
struct
iso_inode_info
isofs_i;
struct
nfs_inode_info
nfs_i;
}
u;
};struct
file主要用于與文件系統(tǒng)對(duì)應(yīng)的設(shè)備驅(qū)動(dòng)程序使用。當(dāng)然,其它設(shè)備驅(qū)動(dòng)程序也可以使用它。它提供關(guān)于被打開(kāi)的文件的信息,定義為:#include
<linux/fs.h>struct
file
{
mode_t
f_mode;
dev_t
f_rdev;
//
needed
for
/dev/tty
off_t
f_pos;
//
Curr.
posn
in
file
unsigned
short
f_flags;
//
The
flags
arg
passed
to
open
unsigned
short
f_count;
//
Number
of
opens
on
this
file
unsigned
short
f_reada;
struct
inode
*f_inode;
//
pointer
to
the
inode
struct
struct
file_operations
*f_op;//
pointer
to
the
fops
struct};在結(jié)構(gòu)file_operations里,指出了設(shè)備驅(qū)動(dòng)程序所提供的入口點(diǎn)位置,分別是:(1)
lseek,移動(dòng)文件指針的位置,顯然只能用于可以隨機(jī)存取的設(shè)備。(2)
read,進(jìn)行讀操作,參數(shù)buf為存放讀取結(jié)果的緩沖區(qū),count為所要讀取的數(shù)據(jù)長(zhǎng)度。返回值為負(fù)表示讀取操作發(fā)生錯(cuò)誤,否則返回實(shí)際讀取的字節(jié)數(shù)。對(duì)于字符型,要求讀取的字節(jié)數(shù)和返回的實(shí)際讀取字節(jié)數(shù)都必須是inode->i_blksize的的倍數(shù)。(3)
write,進(jìn)行寫操作,與read類似。(4)
readdir,取得下一個(gè)目錄入口點(diǎn),只有與文件系統(tǒng)相關(guān)的設(shè)備驅(qū)動(dòng)程序才使用。(5)
selec,進(jìn)行選擇操作,如果驅(qū)動(dòng)程序沒(méi)有提供select入口,select操作將會(huì)認(rèn)為設(shè)備已經(jīng)準(zhǔn)備好進(jìn)行任何的I/O操作。(6)
ioctl,進(jìn)行讀、寫以外的其它操作,參數(shù)cmd為自定義的的命令。(7)
mmap,用于把設(shè)備的內(nèi)容映射到地址空間,一般只有塊設(shè)備驅(qū)動(dòng)程序使用。(8)
open,打開(kāi)設(shè)備準(zhǔn)備進(jìn)行I/O操作。返回0表示打開(kāi)成功,返回負(fù)數(shù)表示失敗。如果驅(qū)動(dòng)程序沒(méi)有提供open入口,則只要/dev/driver文件存在就認(rèn)為打開(kāi)成功。(9)
release,即close操作。設(shè)備驅(qū)動(dòng)程序所提供的入口點(diǎn),在設(shè)備驅(qū)動(dòng)程序初始化的時(shí)候向系統(tǒng)進(jìn)行登記,以便系統(tǒng)在適當(dāng)?shù)臅r(shí)候調(diào)用。Linux系統(tǒng)里,通過(guò)調(diào)用register_chrdev向系統(tǒng)注冊(cè)字符型設(shè)備驅(qū)動(dòng)程序。register_chrdev定義為:
#include
<linux/fs.h>
#include
<linux/errno.h>
int
register_chrdev(unsigned
int
major,
const
char
*name,
struct
file_operations
*fops);
其中,major是為設(shè)備驅(qū)動(dòng)程序向系統(tǒng)申請(qǐng)的主設(shè)備號(hào),如果為0則系統(tǒng)為此驅(qū)動(dòng)程序動(dòng)態(tài)地分配一個(gè)主設(shè)備號(hào)。name是設(shè)備名。fops就是前面所說(shuō)的對(duì)各個(gè)調(diào)用的入口點(diǎn)的說(shuō)明。此函數(shù)返回0表示成功。返回-EINVAL表示申請(qǐng)的主設(shè)備號(hào)非法,一般來(lái)說(shuō)是主設(shè)備號(hào)大于系統(tǒng)所允許的最大設(shè)備號(hào)。返回-EBUSY表示所申請(qǐng)的主設(shè)備號(hào)正在被其它設(shè)備驅(qū)動(dòng)程序使用。如果是動(dòng)態(tài)分配主設(shè)備號(hào)成功,此函數(shù)將返回所分配的主設(shè)備號(hào)。如果register_chrdev操作成功,設(shè)備名就會(huì)出現(xiàn)在/proc/devices文件里。初始化部分一般還負(fù)責(zé)給設(shè)備驅(qū)動(dòng)程序申請(qǐng)系統(tǒng)資源,包括內(nèi)存、中斷、時(shí)鐘、I/O端口等,這些資源也可以在open子程序或別的地方申請(qǐng)。在這些資源不用的時(shí)候,應(yīng)該釋放它們,以利于資源的共享。在Unix系統(tǒng)里,對(duì)中斷的處理是屬于系統(tǒng)核心的部分,因此如果設(shè)備與系統(tǒng)之間以中斷方式進(jìn)行數(shù)據(jù)交換的話,就必須把該設(shè)備的驅(qū)動(dòng)程序作為系統(tǒng)核心的一部分。設(shè)備驅(qū)動(dòng)程序通過(guò)調(diào)用request_irq函數(shù)來(lái)申請(qǐng)中斷,通過(guò)free_irq來(lái)釋放中斷。它們的定義為:#include
<linux/sched.h>int
request_irq(unsigned
int
irq,
void
(*handler)(int
irq,void
dev_id,struct
pt_regs
*regs),
unsigned
long
flags,
const
char
*device,
void
*dev_id);void
free_irq(unsigned
int
irq,
void
*dev_id);參數(shù)irq表示所要申請(qǐng)的硬件中斷號(hào)。handler為向系統(tǒng)登記的中斷處理子程序,中斷產(chǎn)生時(shí)由系統(tǒng)來(lái)調(diào)用,調(diào)用時(shí)所帶參數(shù)irq為中斷號(hào),dev_id為申請(qǐng)時(shí)告訴系統(tǒng)的設(shè)備標(biāo)識(shí),regs為中斷發(fā)生時(shí)寄存器內(nèi)容。device為設(shè)備名,將會(huì)出現(xiàn)在/proc/interrupts文件里。flag是申請(qǐng)時(shí)的選項(xiàng),它決定中斷處理程序的一些特性,其中最重要的是中斷處理程序是快速處理程序(flag里設(shè)置了SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT),快速處理程序運(yùn)行時(shí),所有中斷都被屏蔽,而慢速處理程序運(yùn)行時(shí),除了正在處理的中斷外,其它中斷都沒(méi)有被屏蔽。在Linux系統(tǒng)中,中斷可以被不同的中斷處理程序共享,這要求每一個(gè)共享此中斷的處理程序在申請(qǐng)中斷時(shí)在flags里設(shè)置SA_SHIRQ,這些處理程序之間以dev_id來(lái)區(qū)分。如果中斷由某個(gè)處理程序獨(dú)占,則dev_id可以為NULL。request_irq返回0表示成功,返回-INVAL表示irq>15或handler==NULL,返回-EBUSY表示中斷已經(jīng)被占用且不能共享。作為系統(tǒng)核心的一部分,設(shè)備驅(qū)動(dòng)程序在申請(qǐng)和釋放內(nèi)存時(shí)不是調(diào)用malloc和free,而代之以調(diào)用kmalloc和kfree,它們被定義為:#include
<linux/kernel.h>void
*
kmalloc(unsigned
int
len,
int
priority);void
kfree(void
*
obj);參數(shù)len為希望申請(qǐng)的字節(jié)數(shù),obj為要釋放的內(nèi)存指針。priority為分配內(nèi)存操作的優(yōu)先級(jí),即在沒(méi)有足夠空閑內(nèi)存時(shí)如何操作,一般用GFP_KERNEL。與中斷和內(nèi)存不同,使用一個(gè)沒(méi)有申請(qǐng)的I/O端口不會(huì)使CPU產(chǎn)生異常,也就不會(huì)導(dǎo)致諸如“segmentation
fault"一類的錯(cuò)誤發(fā)生。任何進(jìn)程都可以訪問(wèn)任何一個(gè)I/O端口。此時(shí)系統(tǒng)無(wú)法保證對(duì)I/O端口的操作不會(huì)發(fā)生沖突,甚至?xí)虼硕瓜到y(tǒng)崩潰。因此,在使用I/O端口前,也應(yīng)該檢查此I/O端口是否已有別的程序在使用,若沒(méi)有,再把
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度農(nóng)業(yè)保險(xiǎn)代理與服務(wù)合同
- 2025年度高端制造裝備研發(fā)股權(quán)投資及市場(chǎng)拓展合同
- 二零二五年度南昌商品房買賣合同2025版標(biāo)準(zhǔn)文本
- 2025年度個(gè)人門面出租合同附贈(zèng)增值服務(wù)范本3篇
- 2025年度鋼材運(yùn)輸服務(wù)合同模板
- 二零二五年度跨境電商進(jìn)口生鮮食品采購(gòu)合同范本4篇
- 華為認(rèn)證智能協(xié)作中級(jí) HCIP-Collaboration H11-861考試題庫(kù)及答案
- 2025年度汽車租賃車輛租賃價(jià)格調(diào)整合同6篇
- 2025年度模具行業(yè)學(xué)徒培養(yǎng)用工合同示范4篇
- 2025年度南匯工商行政管理志編纂服務(wù)合同4篇
- 農(nóng)村自建房安全合同協(xié)議書
- 《教科版》二年級(jí)科學(xué)下冊(cè)全冊(cè)課件(完整版)
- 杜仲葉藥理作用及臨床應(yīng)用研究進(jìn)展
- 4S店售后服務(wù)6S管理新規(guī)制度
- 高性能建筑鋼材的研發(fā)與應(yīng)用
- 無(wú)線廣播行業(yè)現(xiàn)狀分析
- 漢語(yǔ)言溝通發(fā)展量表(長(zhǎng)表)-詞匯及手勢(shì)(8-16月齡)
- 高速公路相關(guān)知識(shí)講座
- 兒科關(guān)于抗生素使用的PDCA
- 小學(xué)生必備古詩(shī)
- 手術(shù)室護(hù)理實(shí)踐指南2023年
評(píng)論
0/150
提交評(píng)論