第八章 設備驅動 操作系統的目的之一就是將系統硬件設備細節從用戶視線中隱藏起來。例如虛擬文件系統對各種類型已安裝的文件系統提供了統一的視圖而屏蔽了具體底層細節。本章將描敘Linux核心對系統中物理設備的管理。 CPU並不是系統中唯一的智能設備,每個物
第八章 設備驅動
操作系統的目的之一就是將系統硬件設備細節從用戶視線中隱藏起來。例如虛擬文件系統對各種類型已安裝的文件系統提供了統一的視圖而屏蔽了具體底層細節。本章將描敘Linux核心對系統中物理設備的管理。
CPU並不是系統中唯一的智能設備,每個物理設備都擁有自己的控制器。鍵盤、鼠標和串行口由一個高級I/O芯片統一管理,IDE控制器控制IDE硬盤而SCSI控制器控制SCSI硬盤等等。每個硬件控制器都有各自的控制和狀態寄存器(CSR)並且各不相同。例如Adaptec 2940 SCSI控制器的CSR與NCR 810 SCSI控制器完全不一樣。這些CSR被用來啟動和停止,初始化設備及對設備進行診斷。在Linux中管理硬件設備控制器的代碼並沒有放置在每個應用程序中而是由內核統一管理。這些處理和管理硬件控制器的軟件就是設備驅動。Linux核心設備驅動是一組運行在特權級上的內存駐留底層硬件處理共享庫。正是它們負責管理各個設備。
設備驅動的一個基本特征是設備處理的抽象概念。所有硬件設備都被看成普通文件;可以通過和操縱普通文件相同的標准系統調用來打開、關閉、讀取和寫入設備。系統中每個設備都用一種特殊的設備相關文件來表示(device special file),例如系統中第一個IDE硬盤被表示成/dev/hda。塊(磁盤)設備和字符設備的設備相關文件可以通過mknod命令來創建,並使用主從設備號來描敘此設備。網絡設備也用設備相關文件來表示,但Linux尋找和初始化網絡設備時才建立這種文件。由同一個設備驅動控制的所有設備具有相同的主設備號。從設備號則被用來區分具有相同主設備號且由相同設備驅動控制的不同設備。 例如主IDE硬盤的每個分區的從設備號都不相同。如/dev/hda2表示主IDE硬盤的主設備號為3而從設備號為2。Linux通過使用主從設備號將包含在系統調用中的(如將一個文件系統mount到一個塊設備)設備相關文件映射到設備的設備驅動以及大量系統表格中,如字符設備表,chrdevs。
Linux支持三類硬件設備:字符、塊及網絡設備。字符設備指那些無需緩沖直接讀寫的設備,如系統的串口設備/dev/cua0和/dev/cua1。塊設備則僅能以塊為單位讀寫,典型的塊大小為512或1024字節。塊設備的存取是通過buffer cache來進行並且可以進行隨機訪問,即不管塊位於設備中何處都可以對其進行讀寫。塊設備可以通過其設備相關文件進行訪問,但更為平常的訪問方法是通過文件系統。只有塊設備才能支持可安裝文件系統。網絡設備可以通過BSD套接口訪問,我們將在網絡一章中討論網絡子系統。
Linux核心中雖存在許多不同的設備驅動但它們具有一些共性:
核心代碼
設備驅動是核心的一部分,象核心中其它代碼一樣,出錯將導致系統的嚴重損傷。一個編寫奇差的設備驅動甚至能使系統崩潰並導致文件系統的破壞和數據丟失。
核心接口
設備驅動必須為Linux核心或者其從屬子系統提供一個標准接口。例如終端驅動為Linux核心提供了一個文件I/O接口而SCSI設備驅動為SCSI子系統提供了一個SCSI設備接口,同時此子系統為核心提供了文件I/O和buffer cache接口。
核心機制與服務
設備驅動可以使用標准的核心服務如內存分配、中斷發送和等待隊列等等。
動態可加載
多數Linux設備驅動可以在核心模塊發出加載請求時加載,同時在不再使用時卸載。這樣核心能有效地利用系統資源。
可配置
Linux設備驅動可以連接到核心中。當核心被編譯時,哪些核心被連入核心是可配置的。
動態性
當系統啟動及設備驅動初始化時將查找它所控制的硬件設備。如果某個設備的驅動為一個空過程並不會有什麼問題。此時此設備驅動僅僅是一個冗余的程序,它除了會占用少量系統內存外不會對系統造成什麼危害。
8.1 輪詢與中斷
設備被執行某個命令時,如“將讀取磁頭移動到軟盤的第42扇區上”,設備驅動可以從輪詢方式和中斷方式中選擇一種以判斷設備是否已經完成此命令。
輪詢方式意味著需要經常讀取設備的狀態,一直到設備狀態表明請求已經完成為止。如果設備驅動被連接進入核心,這時使用輪詢方式將會帶來災難性後果:核心將在此過程中無所事事,直到設備完成此請求。但是輪詢設備驅動可以通過使用系統定時器,使核心周期性調用設備驅動中的某個例程來檢查設備狀態。 定時器過程可以檢查命令狀態及Linux軟盤驅動的工作情況。使用定時器是輪詢方式中最好的一種,但更有效的方法是使用中斷。
基於中斷的設備驅動會在它所控制的硬件設備需要服務時引發一個硬件中斷。如以太網設備驅動從網絡上接收到一個以太數據報時都將引起中斷。Linux核心需要將來自硬件設備的中斷傳遞到相應的設備驅動。這個過程由設備驅動向核心注冊其使用的中斷來協助完成。此中斷處理例程的地址和中斷號都將被記錄下來。在/proc/inter
rupts文件中你可以看到設備驅動所對應的中斷號及類型:
0: 727432 timer
1: 20534 keyboard
2: 0 cascade
3: 79691 + serial
4: 28258 + serial
5: 1 sound blaster
11: 20868 + aic7xxx
13: 1 math error
14: 247 + ide0
15: 170 + ide1
對中斷資源的請求在驅動初始化時就已經完成。作為IBM PC體系結構的遺產,系統中有些中斷已經固定。例如軟盤控制器總是使用中斷6。其它中斷,如PCI設備中斷,在啟動時進行動態分配。設備驅動必須在取得對此中斷的所有權之前找到它所控制設備的中斷號(IRQ)。Linux通過支持標准的PCI BIOS回調函數來確定系統中PCI設備的中斷信息,包括其IRQ號。
如何將中斷發送給CPU本身取決於體系結構,但是在多數體系結構中,中斷以一種特殊模式發送同時還將阻止系統中其它中斷的產生。設備驅動在其中斷處理過程中作的越少越好,這樣Linux核心將能很快的處理完中斷並返回中斷前的狀態中。為了在接收中斷時完成大量工作,設備驅動必須能夠使用核心的底層處理例程或者任務隊列來對以後需要調用的那些例程進行排隊。
8.2 直接內存訪問 (DMA)
數據量比較少時,使用中斷驅動設備驅動程序能順利地在硬件設備和內存之間交換數據。例如波特率為9600的modem可以每毫秒傳輸一個字符。如果硬件設備引起中斷和調用設備驅動中斷所消耗的中斷時延比較大(如2毫秒)則系統的綜合數據傳輸率會很低。則9600波特率modem的數據傳輸只能利用0.002%的CPU處理時間。高速設備如硬盤控制器或者以太網設備數據傳輸率將更高。SCSI設備的數據傳輸率可達到每秒40M字節。
直接內存存取(DMA)是解決此類問題的有效方法。DMA控制器可以在不受處理器干預的情況下在設備和系統內存之間高速傳輸數據。PC機的ISA DMA控制器有8個DMA通道,其中七個可以由設備驅動使用。每個DMA通道具有一個16位的地址寄存器和一個16位的記數寄存器。為了初始化數據傳輸,設備驅動將設置DMA通道地址和記數寄存器以描敘數據傳輸方向以及讀寫類型。然後通知設備可以在任何時候啟動DMA操作。傳輸結束時設備將中斷PC。在傳輸過程中CPU可以轉去執行其他任務。
設備驅動使用DMA時必須十分小心。首先DMA控制器沒有任何虛擬內存的概念,它只存取系統中的物理內存。同時用作DMA傳輸緩沖的內存空間必須是連續物理內存塊。這意味著不能在進程虛擬地址空間內直接使用DMA。但是你可以將進程的物理頁面加鎖以防止在DMA操作過程中被交換到交換設備上去。另外DMA控制器所存取物理內存有限。DMA通道地址寄存器代表DMA地址的高16位而頁面寄存器記錄的是其余8位。所以DMA請求被限制到內存最低16M字節中。
DMA通道是非常珍貴的資源,一共才有7個並且還不能夠在設備驅動間共享。與中斷一樣,設備驅動必須找到它應該使用那個DMA通道。有些設備使用固定的DMA通道。例如軟盤設備總使用DMA通道2。有時設備的DMA通道可以由跳線來設置,許多以太網設備使用這種技術。設計靈活的設備將告訴系統它將使用哪個DMA通道,此時設備驅動僅需要從DMA通道中選取即可。
Linux通過dma_chan(每個DMA通道一個)數組來跟蹤DMA通道的使用情況。dma_chan結構中包含有兩個域,一個是指向此DMA通道擁有者的指針,另一個指示DMA通道是否已經被分配出去。當敲入cat /proc/dma打印出來的結果就是dma_chan結構數組。
8.3 內存
設備驅動必須謹慎使用內存。由於它屬於核心,所以不能使用虛擬內存。系統接收到中斷信號時或調度底層任務隊列處理過程時,設備驅動將開始運行,而當前進程會發生改變。設備驅動不能依賴於任何運行的特定進程,即使當前是為該進程工作。與核心的其它部分一樣,設備驅動使用數據結構來描敘它所控制的設備。這些結構被設備驅動代碼以靜態方式分配,但會增大核心而引起空間的浪費。多數設備驅動使用核心中非頁面內存來存儲數據。
Linux為設備驅動提供了一組核心內存分配與回收過程。核心內存以2的次冪大小的塊來分配。如512或128字節,此時即使設備驅動的
需求小於這個數量也會分配這麼多。所以設備驅動的內存分配請求可得到以塊大小為邊界的內存。這樣核心進行空閒塊組合更加容易。
請求分配核心內存時Linux需要完成許多額外的工作。如果系統中空閒內存數量較少,則可能需要丟棄些物理頁面或將其寫入交換設備。一般情況下Linux將掛起請求者並將此進程放置到等待隊列中直到系統中有足夠的物理內存為止。不是所有的設備驅動(或者真正的Linux核心代碼)都會經歷這個過程,所以如分配核心內存的請求不能立刻得到滿足,則此請求可能會失敗。如果設備驅動希望在此內存中進行DMA,那麼它必須將此內存設置為DMA使能的。這也是為什麼是Linux核心而不是設備驅動需要了解系統中的DMA使能內存的原因。
8.4 設備驅動與核心的接口
Linux核心與設備驅動之間必須有一個以標准方式進行互操作的接口。每一類設備驅動:字符設備、塊設備 及網絡設備都提供了通用接口以便在需要時為核心提供服務。這種通用接口使得核心可以以相同的方式來對待不同的設備及設備驅動。如SCSI和IDE硬盤的區別很大但Linux對它們使用相同的接口。
Linux動態性很強。每次Linux核心啟動時如遇到不同的物理設備將需要不同的物理設備驅動。Linux允許通過配置腳本在核心重建時將設備驅動包含在內。設備驅動在啟動初始化時可能會發現系統中根本沒有任何硬件需要控制。其它設備驅動可以在必要時作為核心模塊動態加載到。為了處理設備驅動的動態屬性,設備驅動在初始化時將其注冊到核心中去。Linux維護著已注冊設備驅動表作為和設備驅動的接口。這些表中包含支持此類設備例程的指針和相關信息。
8.4.1 字符設備
圖8.1 字符設備
字符設備是Linux設備中最簡單的一種。應用程序可以和存取文件相同的系統調用來打開、讀寫及關閉它。即使此設備是將Linux系統連接到網絡中的PPP後台進程的modem也是如此。字符設備初始化時,它的設備驅動通過在device_struct結構的chrdevs數組中添加一個入口來將其注冊到Linux核 心上。設備的主設備標志符用來對此數組進行索引(如對tty設備的索引4)。設備的主設備標志符是固定的。
chrdevs數組每個入口中的device_struct數據結構包含兩個元素;一個指向已注冊的設備驅動名稱,另一個則是指向一組文件操作指針。它們是位於此字符設備驅動內部的文件操作例程的地址指針,用來處理相關的文件操作如打開、讀寫與關閉。/proc/devices中字符設備的內容來自chrdevs數組。
當打開代表字符設備的字符特殊文件時(如/dev/cua0),核心必須作好准備以便調用相應字符設備驅動的文件操作例程。與普通的目錄和文件一樣,每個字符特殊文件用一個VFS節點表示。每個字符特殊文件使用的VFS inode和所有設備特殊文件一樣,包含著設備的主從標志符。這個VFS inode由底層的文件系統來建立(比如EXT2),其信息來源於設備相關文件名稱所在文件系統。
每個VFS inode和一組文件操作相關聯,它們根據inode代表的文件系統對象變化而不同。當創建一個代表字符相關文件的VFS inode時,其文件操作被設置為缺省的字符設備操作。
字符設備只有一個文件操作:打開文件操作。當應用打開字符特殊文件時,通用文件打開操作使用設備的主標志符來索引此chrdevs數組,以便得到那些文件操作函數指針。同時建立起描敘此字符特殊文件的file結構,使其文件操作指針指向此設備驅動中的文件操作指針集合。這樣所有應用對它進行的文件操作都被映射到此字符設備的文件操作集合上。
8.4.2 塊設備
塊設備也支持以文件方式訪問。系統對塊設備特殊文件提供了非常類似於字符特殊文件的文件操作機制。Linux在blkdevs數組中維護所有已注冊的塊設備。象chrdevs數組一樣,blkdevs也使用設備的主設備號進行索引。其入口也是device_struct結構。和字符設備不同的是系統有幾類塊設備。SCSI設備是一類而IDE設備則是另外一類。它們將以各自類別登記到Linux核心中並為核心提供文件操作功能。某類塊設備的設備驅動為此類型設備提供了類別相關的接口。如SCSI設備驅動必須為SCSI子系統提供接口以便SCSI子系統能用它來為核心提供對此設備的文件操作。
和普通文件操作接口一樣, 每個塊設備驅動必須為buffer cache提供接口。每個塊設備驅動將填充其在blk_dev數組中的blk_dev_struct結構入口。數組的索引值還是此設備的主設備號。這個blk_dev_struct結構包含請求過程的地址以及指向請求數據結構鏈表的指針,每個代表一個從buffer cache中來讓設備進行數據讀寫的請求。
圖8.2 buffer cache塊設備請求
每當buffer cache希望從一個已注冊設備中讀寫數據塊時,它會將request結構添加到其blk_dev_struct中。圖8.2表示每個請求有指向一個或多個buffer_hear結構的指針,每個請求讀寫一塊數據。如buffer cache對buffer_head結構上鎖, 則進程會等待到對此緩沖的塊操作完成。每個request結構都從靜態鏈表all_requests中分配。如果此請求被加入到空請求鏈表中,則將調用驅動請求函數以啟動此請求隊列的處理,否則該設備驅動將簡單地處理請求鏈表上的request 。
一旦設備驅動完成了請求則它必須將每個buffer_heard結構從request結構中清除,將它們標記成已更新狀態並解鎖之。對buffer_head的解鎖將喚醒所有等待此塊操作完成的睡眠進程。如解析文件名稱時,EXT2文件系統必須從包含此文件系統的設備中讀取包含下個EXT2目錄入口的數據塊。在buffer_head上睡眠的進程在設備驅動被喚醒後將包含此目錄入口。request數據結構被標記成空閒以便被其它塊請求使用。
8.5 硬盤
磁盤驅動器提供了一個永久性存儲數據的方式,將數據保存在旋轉的盤片上。寫入數據時磁頭將磁化盤片上的一個小微粒。這些盤片被連接到一個中軸上並以3000到10,000RPM(每分鐘多少轉)的恆定速度旋轉。而軟盤的轉速僅為360RPM。磁盤的讀/寫磁頭負責讀寫數據,每個盤片的兩側各有一個磁頭。磁頭讀寫時並不接觸盤片表面而是浮在距表面非常近的空氣墊中(百萬分之一英寸)。磁頭由一個馬達驅動在盤片表面移動。所有的磁頭被連在一起,它們同時穿過盤片的表面。
盤片的每個表面都被劃分成為叫做磁道的狹窄同心圓。0磁道位於最外面而最大磁道位於最靠近中央主軸。柱面指一組相同磁道號的磁道。所以每個盤片上的第五磁道組成了磁盤的第五柱面。由於柱面號與磁道號相等所以我們經常可以看到以柱面描敘的磁盤布局。每個磁道可進一步劃分成扇區。它是硬盤數據讀寫的最小單元同時也是磁盤的塊大小。一般的扇區大小為512字節並且這個大小可以磁盤制造出來後格式化時設置。
一個磁盤經常被描繪成有多少各柱面、磁頭以及扇區。例如系統啟動時Linux將這樣描敘一個IDE硬盤:
hdb: Conner Peripherals 540MB - CFS540A, 516MB w/64kB Cache, CHS=1050/16/63
這表示此磁盤有1050各柱面(磁道),16個磁頭(8個盤片)且每磁道包含63個扇區。這樣我們可以通過扇區數、塊數以及512字節扇區大小計算出磁盤的存儲容量為529200字節。這個容量和磁盤自身聲稱的516M字節並不相同,這是因為有些扇區被用來存放磁盤分區信息。有些磁盤還能自動尋找壞扇區並重新索引磁盤以正常使用。
物理硬盤可進一步劃分成分區。一個分區是一大組為特殊目的而分配的扇區。對磁盤進行分區使得磁盤可以同時被幾個操作系統或不同目的使用。許多Linux系統具有三個分區:DOS文件系統分區,EXT2文件系統分區和交換分區。硬盤分區用分區表來描敘;表中每個入口用磁頭、扇區及柱面號來表示分區的起始與結束。對於用DOS格式化的硬盤有4個主分區表。但不一定所有的四個入口都被使用。fdisk 支持3中分區類型:主分區、擴展分區及邏輯分區。擴展分區並不是真正的分區,它只不過包含了幾個邏輯分區。擴展和邏輯分區用來打破四個主分區的限制。以下是一個包含兩個主分區的fdisk命令的輸出:
Disk /dev/sda: 64 heads, 32 sectors, 510 cylinders
Units = cylinders of 2048 * 512 bytes
Device Boot Begin Start End Blocks Id System
/dev/sda1 1 1 478 489456 83 Linux native
/dev/sda2 479 479 510 32768 82 Linux swap
Expert command (m for help): p
Disk /dev/sda: 64 heads, 32 sectors, 510 cylinders
Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID
1 00 1 1 0 63 32 477 32 978912 83
2 00 0 1 478 63 32 509 978944 65536 82
3 00 0 0 0 0 0 0 0 0 00
4 00 0 0 0 0 0 0 0 0 00
這些內容表明第一個分區從柱面(或者磁道)0,頭1和扇區1開始一直到柱面477,扇區22和頭63結束。 由於每磁道有32個扇區且有64個讀寫磁頭則此分區在大小上等於柱面數。fdisk使分區在柱面邊界上對齊。 它從最外面的柱面0開始並向中間擴展478個柱面。第二個分區:交換分區從478號柱面開始並擴展到磁盤的最內圈。
圖8.3 磁盤鏈表
在初始化過程中Linux取得系統中硬盤的拓撲結構映射。它找出有多少中硬盤以及是什麼類型。另外Linux 還要找到每個硬盤的分區方式。所有這些都用由gendisk_head鏈指針指向的gendisk結構鏈表來表示。每個磁盤子系統如IDE在初始化時產生表示磁盤結構的gendisk結構。同時它將注冊其文件操作例程並將此入口添加到blk_dev數據結構中。每個gendisk結構包含唯一的主設備號,它與塊相關設備的主設備號相同。例如SCSI磁盤子系統創建了一個主設備號為8的gendisk入口("sd"),這也是所有SCSI硬盤設備的主設備號。圖8.3給出了兩個gendisk入口,一個表示SCSI磁盤子系統而另一個表示IDE磁盤控制器。ide0表示主IDE控制器。
盡管磁盤子系統在其初始化過程中就建立了gendisk入口, 但是只有Linux作分區檢查時才使用。每個磁盤子系統通過維護一組數據結構將物理硬盤上的分區與某個特殊主從特殊設備互相映射。無論何時通過 buffer cache或文件操作對塊設備的讀寫都將被核心定向到對具有某個特定主設備號的設備文件上(如 /dev/sda2)。而從設備號的定位由各自設備驅動或子系統來映射。
8.5.1 IDE 硬盤
Linux系統上使用得最廣泛的硬盤是集成電子磁盤或者IDE硬盤。IDE是一個硬盤接口而不是類似SCSI的I/O總線接口。每個IDE控制器支持兩個硬盤,一個為主另一個為從。主從硬盤可以通過盤上的跳線來設置。系統中的第一個IDE控制器成為主IDE控制器而另一個為從屬控制器。IDE可以以每秒3.3M字節的傳輸率傳輸數據且最大容量為538M字節。EIDE或增強式IDE可以將磁盤容量擴展到8.6G字節而數據傳輸率為16.6M字節/秒。由於IDE和EIDE都比SCSI硬盤便宜, 所以大多現代PC機在包含一個或幾個板上IDE控制器。
Linux以其發現控制器的順序來對IDE硬盤進行命名。在主控制器中的主盤為/dev/hda而從盤為/dev/hdb。/dev/hdc用來表示從屬IDE控制器中的主盤。IDE子系統將向Linux核心注冊IDE控制器而不是IDE硬盤。主IDE控制器的主標志符為3而從屬IDE控制器的主標志符為22。如果系統中包含兩個IDE控制器則IDE子系統的入口在blk_dev和blkdevs數組的第2和第22處。IDE的塊設備文件反應了這種編號方式,硬盤 /dev/hda和/dev/hdb都連接到主IDE控制器上,其主標志符為3。對IDE子系統上這些塊相關文件的文件或者buffer cache的操作都通過核心使用主設備標志符作為索引定向到IDE子系統上。當發出請求時,此請求由哪個IDE硬盤來完成取決於IDE子系統。為了作到這一點IDE子系統使用從設備編號對應的設備特殊標志符,由它包含的信息來將請求發送到正確的硬盤上。位於主IDE控制器上的IDE從盤/dev/hdb的設備標志符為(3,64)。而此盤中第一個分區(/dev/hdb1)的設備標志符為(3,65)。
8.5.2 初始化IDE子系統
IDE磁盤與IBM PC關系非常密切。在這麼多年中這些設備的接口發生了變化。這使得IDE子系統的初始化過程比看上去要復雜得多。
Linux可以支持的最多IDE控制器個數為4。每個控制器用ide_hwifs數組中的ide_hwif_t結構來表示。每個ide_hwif_t結構包含兩個ide_drive_t結構以支持主從IDE驅動器。在IDE子系統的初始化過程中Linux通過訪問系統CMOS來判斷是否有關於硬盤的信息。這種CMOS由電池供電所以系統斷電時也不會遺失其中的內容。它 位於永不停止的系統實時時鐘設備中。此CMOS內存的位置由系統BIOS來設置,它將通知Linux系統中有多少個IDE控制器與驅動器。Linux使用這些從BIOS中發現的磁盤數據來建立對應此驅動器的ide_hwif_t結構。 許多現代PC系統使用PCI芯片組如Intel 82430 VX芯片組將PCI EIDE控制器封裝在內。IDE子系統使用PCI BIOS回調函數來定位系統中PCI (E)IDE控制器。然後對這些芯片組調用PCI特定查詢例程。
每次找到一個IDE接口或控制器就有建立一個ide_hwif_t結構來表示控制器和與之相連的硬盤。在操作過程中IDE驅動器對I/O內存空間中的IDE命令寄存器寫入命令。主IDE控制器的缺省控制和狀態寄存器是0x1F0 - 0x1F7。這個地址由早期的IBM PC規范設定。IDE驅動器為每個控制器向Linux注冊塊緩沖cache和VFS節點並將其加入到blk_dev和blkdevs數組中。IDE驅動器需要申請某個中斷。一般主IDE控制器中斷號為14而從屬IDE控制器為15。然而這些都可以通過命令行選項由核心來重載。IDE驅動器同時還將gendisk入口加入到啟動時發現的每個IDE控制器的gendisk鏈表中去。分區檢查代碼知道每個IDE控制器可能包含兩個IDE硬盤。
8.5.3 SCSI 硬盤
SCSI(小型計算機系統接口)總線是一種高效的點對點數據總線,它最多可以支持8個設備,其中包括多個主設備。每個設備有唯一的標志符並可以通過盤上的跳線來設置。在總線上的兩個設備間數據可以以同步或異步方式,在32位數據寬度下傳輸率為40M字節來交換數據。SCSI總線上可以在設備間同時傳輸數據與狀態信息。initiator設備和target設備間的執行步驟最多可以包括8個不同的階段。你可以從總線上5個信號來分辨SCSI總線的當前階段。這8個階段是:
BUS FREE
當前沒有設備在控制總線且總線上無事務發生。
ARBITRATION
一個SCSI設備試圖取得SCSI總線的控制權,這時它將其SCSI標志符放置到地址引腳上。具有最高SCSI標志符編號的設備將獲得總線控制權。
SELECTION
當設備通過仲裁成功地取得了對SCSI總線的控制權後它必須向它准備發送命令的那個SCSI設備發出信號。具體做法是將目標設備的SCSI標志符放置在地址引腳上進行聲明。
RESELECTION
在一個請求的處理過程中SCSI設備可能會斷開連接。目標(target)設備將再次選擇啟動設備 (initiator)。不是所有的SCSI設備都支持此階段。
COMMAND
此階段中initiator設備將向target設備發送6、10或12字節命令。
DATA IN, DATA OUT
此階段中數據將在initiator設備和target設備間傳輸。
STATUS
所有命令完畢後將進入此階段,此時允許target設備向initiator設備發送狀態信息以指示操作成功與否。
MESSAGE IN, MESSAGE OUT
此階段附加信息將在initiator設備和target設備間傳輸。
Linux SCSI子系統由兩個基本部分組成,每個由一個數據結構來表示。
host
一個SCSI host即一個硬件設備:SCSI控制權。NCR 810 PCI SCSI控制權即一種SCSI host。在Linux 系統中可以存在相同類型的多個SCSI控制權,每個由一個單獨的SCSI host來表示。這意味著一個SCSI設備驅動可以控制多個控制權實例。SCSI host總是SCSI命令的initiator設備。
Device
雖然SCSI支持多種類型設備如磁帶機、CD-ROM等等,但最常見的SCSI設備是SCSI磁盤。SCSI設備總是SCSI命令的target。這些設備必須區別對待,例如象CD-ROM或者磁帶機這種可移動設備,Linux 必須檢測介質是否已經移動。不同的磁盤類型有不同的主設備號,這樣Linux可以將塊設備請求發送到正確的SCSI設備。
初始化SCSI子系統
SCSI子系統的初始化非常復雜,它必須反映處SCSI總線及其設備的動態性。Linux在啟動時初始化SCSI子系統。 如果它找到一個SCSI控制器(即SCSI hosts)則會掃描此SCSI總線來找出總線上的所有設備。然後初始化這些設備並通過普通文件和buffer cache塊設備操作使Linux核心的其它部分能使用這些設備。初始化過程分成四個階段:
首先Linux將找出在系統核心連接時被連入核心的哪種類型的SCSI主機適配器或控制器有硬件需要控制。每個 核心中的SCSI host在builtin_scsi_hosts數組中有一個Scsi_Host_Template入口。而Scsi_Host_Template結構中包含執行特定SCSI host操作, 如檢測連到此SCSI host的SCSI設備的例程的入口指針。這些例程在SCSI 子系統進行自我配置時使用同時它們還是支持此host類型的SCSI設備驅動的一部分。每個被檢測的SCSI host, 即與真正SCSI設備連接的host將其自身的Scsi_Host_Template結構添加到活動SCSI hosts的scsi_hosts結構鏈表中去。每個被檢測host類型的實例用一個scsi_hostlist鏈表中的Scsi_Host結構來表示。例如一個包含兩個NCR810 PCI SCSI控制器的系統的鏈表中將有兩個Scsi_Host入口,每個控制器對應一個。每個Scsi_Host 指向一個代表器設備驅動的Scsi_Host_Template。
圖8.4 SCSI數據結構
現在每個SCSI host已經找到,SCSI子系統必須找出哪些SCSI設備連接哪個host的總線。SCSI設備的編號是 從0到7,對於一條SCSI總線上連接的各個設備,其設備編號或SCSI標志符是唯一的。SCSI標志符可以通過設 備上的跳線來設置。SCSI初始化代碼通過在SCSI總線上發送一個TEST_UNIT_READY命令來找出每個SCSI設備。 當設備作出相應時其標志符通過一個ENQUIRY命令來讀取。Linux將從中得到生產廠商的名稱和設備模式以及 修訂版本號。SCSI命令由一個Scsi_Cmnd結構來表示同時這些命令通過調用Scsi_Host_Template結構中的設備 驅動例程傳遞到此SCSI host的設備驅動中。被找到的每個SCSI設備用一個Scsi_Device結構來表示,每個指向 其父Scsi_Host結構。所有這些Scsi_Device結構被添加到scsi_device鏈表中。圖8.4給出了這些主要數據結構 間的關系。
一共有四種SCSI設備類型:磁盤,磁帶機,CD-ROM和普通SCSI設備。每種類型的SCSI設備以不同的主塊設備 類型單獨登記到核心中。如果有多個類型的SCSI設備存在則它們只登記自身。每個SCSI設備類型,如SCSI磁盤 維護著其自身的設備列表。它使用這些表將核心塊操作(file或者buffer cache)定向到正確的設備驅動或 SCSI host上。每種SCSI設備類型用一個Scsi_Device_Template結構來表示。此結構中包含此類型SCSI設備的 信息以及執行各種任務的例程的入口地址。換句話說,如果SCSI子系統希望連接一個SCSI磁盤設備它將調用 SCSI磁盤類型連接例程。如果有多個該種類型的SCSI設備被檢測到則此Scsi_Type_Template結構將被添加到 scsi_devicelist鏈表中。
SCSI子系統的最後一個階段是為每個已登記的Scsi_Device_Template結構調用finish函數。對於SCSI磁盤類型 設備它將驅動所有SCSI磁盤並記錄其磁盤布局。同時還將添加一個表示所有連接在一起的SCSI磁盤的gendisk 結構,如圖8.3。
發送塊設備請求
一旦SCSI子系統初始化完成這些SCSI設備就可以使用了。每個活動的SCSI設備類型將其自身登記到核心以便 Linux正確定向塊設備請求。這些請求可以是通過blk_dev的buffer cache請求也可以是通過blkdevs的文件 操作。以一個包含多個EXT2文件系統分區的SCSI磁盤驅動器為例,當安裝其中一個EXT2分區時系統是怎樣將 核心緩沖請求定向到正確的SCSI磁盤的呢?
每個對SCSI磁盤分區的塊讀寫請求將導致一個新的request結構被添加到對應此SCSI磁盤的blk_dev數組中的 current_request鏈表中。如果此request正在被處理則buffer cache無需作任何工作;否則它必須通知SCSI 磁盤子系統去處理它的請求隊列。系統中每個SCSI磁盤用一個Scsi_Disk結構來表示。例如/dev/sdb1的主設備 號為8而從設備號為17;這樣產生一個索引值1。每個Scsi_Disk結構包含一個指向表示此設備的Scsi_Device 結構。這樣反過來又指向擁有它的Scsi_Host結果。這個來自buffer cache的request結構將被轉換成一個描 敘SCSI命令的Scsi_Cmd結構,這個SCSI命令將發送到此SCSI設備同時被排入表示此設備的Scsi_Host結構。一 旦有適當的數據塊需要讀寫,這些請求將被獨立的SCSI設備驅動來處理。
8.6 網絡設備
網絡設備,即Linux的網絡子系統,是一個發送與接收數據包的實體。它一般是一個象以太網卡的物理設備。 有些網絡設備如loopback設備僅僅是一個用來向自身發送數據的軟件。每個網絡設備都用一個device結構來 表示。網絡設備驅動在核心啟動初始化網絡時將這些受控設備登記到Linux中。device數據結構中包含有有關 設備的信息以及用來支持各種網絡協議的函數地址指針。這些函數主要用來使用網絡設備傳輸數據。設備使用 標准網絡支持機制來將接收到的數據傳遞到適當的協議層。所有傳輸與接收到的網絡數據用一個sk_buff結構 來表示,這些靈活的數據結構使得網絡協議頭可以更容易的添加與刪除。網絡協議層如何使用網絡設備以及 如何使用sk_buff來交換數據將在網絡一章中詳細描敘。本章只討論device數據結構及如何發現與初始化網絡。
device數據結構包含以下有關網絡設備的信息:
Name
與使用mknod命令創建的塊設備特殊文件與字符設備特殊文件不同,網絡設備特殊文件僅在於系統 網絡設備發現與初始化時建立。它們使用標准的命名方法,每個名字代表一種類型的設備。多個 相同類型設備將從0開始記數。這樣以太網設備被命名為/dev/eth0,/dev/eth1,/dev/eth2 等等。 一些常見的網絡設備如下:
/dev/ethN 以太網設備
/dev/slN SLIP設備
/dev/pppN PPP 設備
/dev/lo Loopback 設備
Bus Information
這些信息被設備驅動用來控制設備。irq號表示設備使用的中斷號。base address指任何設備在I/O 內存中的控制與狀態寄存器地址。DMA通道指此網絡設備使用的DMA通道號。所有這些信息在設備初 始化時設置。
Interface Flags
它們描敘了網絡設備的屬性與功能:
IFF_UP 接口已經建立並運行
IFF_BROADCAST 設備中的廣播地址有效
IFF_DEBUG 設備調試被使能
IFF_LOOPBACK 這是一個loopback設備
IFF_POINTTOPOINT 這是點到點連接(SLIP和PPP)
IFF_NOTRAILERS 無網絡追蹤者
IFF_RUNNING 資源已被分配
IFF_NOARP 不支持ARP協議
IFF_PRO
MISC 設備處於混亂的接收模式,無論包地址怎樣它都將接收
IFF_ALLMULTI 接收所有的IP多播幀
IFF_MULTICAST 可以接收IP多播幀
Protocol Information
每個設備描敘它可以被網絡協議層如何使用:
mtu
指不包括任何鏈路層頭在內的,網絡可傳送的最大包大小。這個值被協議層用來選擇適當 大小的包進行發送。
Family
這個family域表示設備支持的協議族。所有Linux網絡設備的族是AF_INET,互聯網地址族。
Type
這個硬件接口類型描敘網絡設備連接的介質類型。Linux網絡設備可以支持多種不同類型的 介質。包括以太網、X.25,令牌環,Slip,PPP和Apple Localtalk。
Addresses
結構中包含大量網絡設備相關的地址,包括IP地址。
Packet Queue
指網絡設備上等待傳輸的sk_buff包隊列。
Support Functions
每個設備支持一組標准的例程,它們被協議層作為設備鏈路層