第八章 設備驅動
操作系統的目的之一就是將系統硬件設備細節從用戶視線中隱藏起來。例如
虛擬文件系統對各種類型已安裝的文件系統提供了統一的視圖而屏蔽了具體
底層細節。本章將描敘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/interrupts文件中你可以看到設備驅動所對應的中斷號及類
型:
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字節,此時即使設備驅
dma_chan結構中包含有兩個域,一個是指向此DMA通道擁有者的指針,另一
個指示DMA通道是否已經被分配出去。當敲入cat/proc/dma打印出來的結果
就是dma_chan結構數組。
8.3 內存
設備驅動必須謹慎使用內存。由於它屬於核心,所以不能使用虛擬內存。系
統接收到中斷信號時或調度底層任務隊列處理過程時,設備驅動將開始運行,
而當前進程會發生改變。設備驅動不能依賴於任何運行的特定進程,即使當
前是為該進程工作。與核心的其它部分一樣,設備驅動使用數據結構來描敘
它所控制的設備。這些結構被設備驅動代碼以靜態方式分配,但會增大核心
而引起空間的浪費。多數設備驅動使用核心中非頁面內存來存儲數據。
Linux為設備驅動提供了一組核心內存分配與回收過程。核心內存以2的次冪
大小的塊來分配。如512或128字節,此時即使設備驅