linux驅動開發的基礎知識

來源:互聯網
上載者:User
Device Drivers (裝置驅動程式) 作業系統其中一個目的就是向使用者掩蓋系統硬體裝置的特殊性。例如,虛擬檔案系統呈現了安裝的檔案系統的一個統一的試圖,而和底層的物理裝置無關。本章描述 Lin ux 核心是如何管理系統中的物理裝置的。 CPU 不是系統中唯一的智慧型裝置,每一個物理裝置都由它自己的硬體控制器。鍵盤、滑鼠和串列口由 SuperIO 晶片控制, IDE 磁碟由 IDE 控制器控制, SCSI 磁碟由 SCSI 控制器控制,等等。每一個硬體控制器都由自己的控制和狀態控制器( CSR ),不同的裝置之間是不同的。一個 Adaptec 2940 SCSI 控制器的 CSR 和 NCR 810 SCSI 控制器的完全不同。 CSR 用於啟動和停止裝置,初始化裝置和診斷它的問題。管理這些硬體控制器的代碼不是放在每一個應用程式裡邊,而是放在 Linux 核心。這些處理或者管理硬體控制器的軟體腳做裝置驅動程式。 Linux 核心的裝置驅動程式本質上是特權的、駐留記憶體的低級的硬體控制常式的共用庫。是 Linux 的裝置驅動程式在處理它們管理的裝置的特質。 UNIX 的一個基本特點是它抽象了裝置的處理。所有的硬體裝置都象常規檔案一樣看待:它們可以使用和操作檔案相同的、標準的系統調用來進行開啟、關閉和讀寫。系統中的每一個裝置都用一個裝置特殊檔案代表。例如系統中第一個 IDE 硬碟用 /dev/had 表示。對於塊(磁碟)和字元裝置,這些裝置特殊檔案用 mknod 命令建立,並使用主( major )和次( minor )裝置編號來描述裝置。網路裝置也用裝置特殊檔案表達,但是它們由 Linux 在找到並初始化系統中的網路控制卡的時候建立。同一個裝置驅動程式控制的所有裝置都由一個共同的 major 裝置編號。次裝置編號用於在不同的裝置和它們的控制器之間進行區分。例如,主 IDE 磁碟的不同分區都由一個不同的次裝置編號。所以, /dev/hda2 ,主 IDE 磁碟的第 2 個分區的主裝置號是 3 ,而次裝置號是 2 。 Linux 使用主裝置號表和一些系統資料表(例如字元裝置表 chrdevs )把系統調用中傳遞的裝置特殊檔案(比如在一個塊裝置上安裝一個檔案系統)映射到這個裝置的裝置驅動程式中。參見 fs/devices.c Linux 支援三類的硬體裝置:字元、塊和網路。字元裝置直接讀寫,沒有緩衝區,例如系統的序列埠 /dev/cua0 和 /dev/cua1 。塊裝置只能按照一個塊(一般是 512 位元組或者 1024 位元組)的倍數進行讀寫。塊裝置通過 buffer cache 訪問,可以隨機存取,就是說,任何塊都可以讀寫而不必考慮它在裝置的什麼地方。塊裝置可以通過它們的裝置特殊檔案訪問,但是更常見的是通過檔案系統進行訪問。只有一個塊裝置可以支援一個安裝的檔案系統。網路裝置通過 BSD socket 介面訪問,網路子系統在網路章(第 10 章)描述。 Linux 有許多不同的裝置驅動程式(這也是 Linux 的力量之一)但是它們都具有一些一 般的屬性: Kernel code 裝置驅動程式和核心中的其他代碼相似,是 kenel 的一部分,如果發生錯誤,可能嚴重損害系統。一個寫錯的驅動程式甚至可能摧毀系統,可能破壞檔案系統,遺失資料。 Kenel interfaces 裝置驅動程式必須向 Linux 核心或者它所在的子系統提供一個標準的介面。例如,終端驅動程式向 Linux 核心提供了一個檔案 I/O 介面,而 SCSI 裝置驅動程式向 SCSI 子系統提供了 SCSI 裝置介面,接著,向核心提供了檔案 I/O 和 bu ffer cache 的介面。 Kernel mechanisms and services 裝置驅動程式使用標準的核心服務例如記憶體配置、中斷轉寄和等待隊列來完成工作 Loadable Linux 大多數的裝置驅動程式可以在需要的時候作為核心模組載入,在不再需要的時候卸載。這使得核心對於系統資源非常具有適應性和效率。 Configurable Linux 裝置驅動程式可以建立在核心。哪些裝置建立到核心在核心編譯的時候是可以配置的。 Dynamic 在系統啟動,每一個裝置啟動程式初始化的時候它尋找它管理的硬體裝置。如果一個裝置驅動程式所控制的裝置不存在並沒有關係。這時這個裝置驅動程式只是多餘的,佔用很少的系統記憶體,而不會產生危害。 8.1 Poling and Interrupts (輪詢和中斷) 每一次給裝置命令的時候,例如“把讀磁頭移到磁碟片的第 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 裝置的中斷,在啟動的時候動態分配。這時裝置驅動程式必須首先找出它所控制的裝置的中斷號,然後才能請求擁有這個中斷(的處理權)。對於 PCI 中斷, Linux 支援標準的 PCI BIOS 回調( callback )來確定系統中裝置的資訊,包括它們的 IRQ 。 一個中斷本身是如何轉寄到 CPU 依賴於體繫結構。但是在大多數的體繫上,中斷都用一種特殊的模式傳遞,而停止系統中發生其他中斷。裝置驅動程式在它的中斷處理常式中應該做儘可能少的工作,使得 Linux 核心可以結束中斷並返回到它中斷之前的地方。收到中斷後需要做大量工作的裝置驅動程式可以使用核心的 bottom half handler 或者任務隊列把常式排在後面,以便在以後調用。 8.2 Direct Memory Access ( DMA ) 當資料量比較少的時候用中斷驅動的裝置驅動程式向裝置或者通過裝置傳輸資料工作地相當好。例如,一個 9600 傳輸速率的 modem 每一毫秒( 1/1000 秒)大約可以傳輸一個字元。如果中斷延遲,就是從硬體裝置發出中斷到開始調用裝置驅動程式中的中斷處理常式所花的時間比較少(比如 2 毫秒),那麼資料轉送對系統整體的映像就非常小。 9600 傳輸速率的 modem 資料傳出只會佔用 0.002% 的 CPU 處理時間。但是對於高速的裝置,比如硬碟控制器或者乙太網路裝置,資料轉送速率相當高。一個 SCSI 裝置每秒可以傳輸高達 40M 位元組的資訊。直接記憶體存取,或者說 DMA ,就是發明來解決這個問題的。一個 DMA 控制器允許裝置不需要處理器的幹預而和系統記憶體創樹資料。 PC 的 ISA DMA 控制器由 8 個 DMA 通道,其中 7 個可用於裝置驅動程式。每一個 DMA 通道都關聯一個 16 位的地址寄存器和一個 16 位的計數寄存器( count register )。為了初始化一次資料轉送,裝置驅動 程式需要建立 DMA 通道的地址和計數寄存器,加上資料轉送的方向,讀或寫。當傳輸結束的時候,裝置中斷 PC 。這樣,傳輸發生的時候, CPU 可以作其他事情。 使用 DMA 的時候裝置驅動程式必須小心。首先,所有的 DMA 控制器都不瞭解虛擬記憶體,它只能訪問系統中的實體記憶體。因此,需要進行 DMA 傳輸的記憶體必須是實體記憶體中連續的塊。這意味著你不能對於一個進程的虛擬位址空間進行 DMA 訪問。但是你可以在執行 DMA 操作的時候把進程的物理也鎖定到記憶體中。第二: DMA 控制器無法訪問全部的實體記憶體。 DMA 通道的地址寄存器表示 DMA 地址的首 16 位,跟著的 8 位來自於頁寄存器( page register )。這意味著 DMA 要求節流在底部的 16M 記憶體中。 DMA 通道是稀少的資源,只有 7 個,又不能在裝置驅動程式之間共用。象中斷一樣,裝置驅動程式必須有能力發現它可以使用哪一個 DMA 通道。象中斷一樣,一些裝置有固定的 DMA 通道。比如軟碟機裝置,總是用 DMA 通道 2 。有時,裝置的 DMA 通道可以用跳線設定:一些乙太網路裝置用這種技術。一些更靈活的裝置可以告訴它(通過它們的 CSR )使用哪一個 DMA 通道,這時,裝置驅動程式可以簡單地找出一個可用的 DMA 通道。 Linux 使用 dma_chan 資料結構向量表(每一個 DMA 通道一個)跟蹤 DMA 通道的使用。 Dma_chan 資料結構只有兩個玉:一個字元指標,描述這個 DMA 通道的屬主,一個標誌顯示這個 DMA 通道是否被分配。當你 cat /proc/dma 的時候顯示的就是 dma_c han 向量表。 8.3 Memory (記憶體) 裝置驅動程式必須小心使用記憶體。因為它們是 Linux 核心的一部分,它們不能使用虛擬記憶體。每一次裝置驅動程式啟動並執行時候,可能是接收到了中斷或者調度了一個 but tom half handler 或任務隊列,當前的進程都可能改變。裝置驅動程式不能依賴於一個 正在啟動並執行特殊進程。象核心中其他部分一樣,裝置驅動程式使用資料結構跟蹤它控制的裝置。這些資料結構可以在裝置驅動程式的代碼部分靜態分配,但是這會讓核心不必要地增大而浪費。多數裝置驅動程式分配核心的、不分頁的記憶體存放它們的資料。 Linux 核心提供了核心的記憶體配置和釋放常式,裝置驅動程式正是使用了這些常式。核心記憶體按照 2 的冪數的塊進行分配。例如 128 或 512 位元組,即使裝置驅動程式請求的數量沒有這麼多。裝置驅動程式請求的位元組數按照下一個塊的大小取整。這使得核心的記憶體回收更容易,因為較小的空閑塊可以組合成更大的塊。 請求核心記憶體的時候 Linux 還需要做更多的附加工作。如果空閑記憶體的總數太少,物理頁需要廢棄或者寫到交換裝置。通常, Linux 會掛起要求者,把這個進程放到一個等待隊列,直到有了足夠的實體記憶體。不是所有的裝置驅動程式(或者實際是 Linux 的核心代碼)希望發生這樣的事情,核心記憶體配置常式可以請求如果不能立刻分配記憶體就失敗。如果裝置驅動程式希望為 DMA 訪問分配記憶體,它也需要指出這塊記憶體是可以進行 DMA 的。因為需要讓 Linux 核心明白系統中哪些是連續的可以進行 DMA 的記憶體,而不是讓裝置驅動程式決定。 8.4 Interfacing Device Drivers with the Kernel (裝置驅動程式和核心介面) Linux 核心必須能夠用標準的方式和它們作用。每一類的裝置驅動程式:字元、塊和網路,都提供了通用的介面供核心在需要請求它們的服務的時候使用。這些通用的介面意味著核心可以完全相同地看待通常是非常不同的裝置和它們的裝置驅動程式。例如, SCSI 和 IDE 磁碟的行為非常不同,但是 Linux 核心對它們使用相同的介面。 Linux 非常地動態,每一次 Linux 核心啟動,它都可能遇到不同的物理裝置從而需要不同的裝置驅動程式。 Linux 允許你在核心建立的時間通過配置指令碼包含裝置驅動程式。當啟動的時候這些裝置驅動程式初始化,它們可能沒發現它們可以控制的任何硬體 。其他驅動程式可以在需要的時候作為核心模組載入。為了處理裝置驅動程式的這種動態特質,裝置驅動程式在它們初始化的時候向核心登記。 Linux 維護已經登記的裝置驅動程式列表,作為和它們介面的一部分。這些列表包括了常式的指標和支援這一類裝置的介面的資訊。 8.4.1 Character Devices (字元裝置) 字元裝置, Linux 最簡單的裝置,象檔案一樣訪問。應用程式使用標準系統調用開啟、讀取、寫和關閉,完全好像這個裝置是一個普通檔案一樣。甚至串連一個 Linux 系統上網的 PPP 守護進程使用的 modem ,也是這樣的。當字元裝置初始化的時候,它的裝置驅動程式向 Linux 核心登記,在 chrdevs 向量表增加一個 device_struct 資料結構條目。這個裝置的主裝置標識符(例如對於 tty 裝置是 4 ),用作這個向量表的索引。一個裝置的主裝置標識符是固定的。 Chrdevs 向量表中的每一個條目,一個 devi ce_struct 資料結構,包括兩個元素:一個登記的裝置驅動程式的名稱的指標和一個指向一組檔案操作的指標。這塊檔案操作本身位於這個裝置的字元裝置驅動程式中,每一個都處理特定的檔案操作比如開啟、讀、寫和關閉。 /proc/devices 中字元裝置的內容來自 chrdevs 向量表參見 include/linux/major.h 當代表一個字元裝置(例如 /dev/cua0 )的字元特殊檔案開啟,核心必須做一些事情,從而去掉用正確的字元裝置驅動程式的檔案操作常式。和普通檔案或目錄一樣,每一個裝置特殊檔案都用 VFS I 節點表達。這個字元特殊檔案的 VFS inode (實際上所有的裝置特殊檔案)都包括裝置的 major 和 minor 標識符。這個 VFS I 節點由底層的檔案系統(例如 EXT2 ),在尋找這個裝置特殊檔案的時候根據實際的檔案系統建立。 參見 fs/ext2/inode.c ext2_read_inode() 每一個 VFS I 節點都聯絡著一組檔案操作,依賴於 I 節點所代表的檔案系統對象不同而不同。不管代表一個字元特殊檔案的 VFS I 節點什麼時候建立,它的檔案操作被設定成字元裝置的預設操作。這隻有一種檔案操作: open 操作。當一個應用程式開啟這個字元特殊檔案的時候,通用的 open 檔案操作使用裝置的主裝置標識符作為 chrde vs 向量表中的索引,取出這種特殊裝置的檔案操作塊。它也建立描述這個字元特殊檔案的 file 資料結構,讓它的檔案操作指向裝置驅動程式中的操作。然後應用程式所有的檔案系統操作都被映射到字元裝置的檔案操作。參見 fs/devices.c chrdev_open() def_chr_fops 8.4.2 Block Devices (塊裝置) 塊裝置也支援象檔案一樣被訪問。這種為開啟的塊特殊檔案提供正確的檔案操作組的機制和字元裝置的十分相似。 Linux 用 blkdevs 向量表維護已經登記的塊裝置檔案。它象 chrdevs 向量表一樣,使用裝置的主裝置號作為索引。它的條目也是 device_s truct 資料結構。和字元裝置不同,塊裝置進行分類。 SCSI 是其中一類,而 IDE 是另一類。類向 Linux 核心登記並向核心提供檔案操作。一種塊裝置類的裝置驅動程式向這種類提供和類相關的介面。例如, SCSI 裝置驅動程式必須向 SCSI 子系統提供介面,讓 SCSI 子系統用來對核心提供這種裝置的檔案操作參見 fs/devices.c 每一個塊裝置驅動程式必須提供普通的檔案操作介面和對於 buffer cache 的介面。每一個塊裝置驅動程式填充 blk_dev 向量表中它的 blk_dev_struct 資料結構。這個向量表的索引還是裝置的主裝置號。這個 blk_dev_struct 資料結構包括一個請求常式的地址和一個指標,指向一個 request 資料結構的列表,每一個都表達 buffer cache 向裝置讀寫一塊資料的一個請求。參見 drivers/block/ll_rw_blk.c include/linux/blkdev.h 每一次 buffer cache 希望讀寫一塊資料到或從一個登記的裝置的時候它就在它的 blk_dev_struc 中增加一個 request 資料結構。圖 8.2 顯示了每一個 request 都有一個指標指向一個或多個 buffer_head 資料結構,每一個都是一個讀寫一塊資料的請求。這個 buffer_head 資料結構被鎖定( buffer cache ),可能會有一個進程在等待這個緩衝區的阻塞進程完成。每一個 request 結構都是從一個靜態表, all_request 表中分配的。如果這個 request 增加到一個空的 request 列表,就調用驅動程式的 req uest 函數處理這個 request 隊列。否則,驅動程式只是簡單地處理 request 隊列中的每一個請求。 一旦裝置驅動程式完成了一個請求,它必須把每一個 buffer_head 結構從 reques t 結構中刪除,標記它們為最新的,然後解鎖。對於 buffer_head 的解鎖會喚醒任何正在等待這個阻塞操作完成的進程。這樣的例子包括檔案解析的時候:必須等待 EXT2 檔案系統從包括這個檔案系統的塊裝置上讀取包括下一個 EXT2 目錄條目的資料區塊,這個進程將會在將要包括目錄條目的 buff_head 隊列中睡眠,直到裝置驅動程式喚醒它。這個 request 資料結構會被標記為空白閑,可以被另一個塊請求使用。 8.5 Hard Disks (硬碟) 硬碟把資料存放在轉動的磁碟上,提供了一個更永久儲存資料的方式。為了寫入資料,微小的磁頭把磁碟表面的一個微小的點磁化。通過磁頭可以探測指定的微粒是否被磁化,從而可以讀出資料。 一個磁碟機由一個或多個磁碟組成,每一個都用相當光滑的玻璃或者陶瓷製成,並覆蓋上一層精細的金屬氧化物。磁碟放在一個中心軸上面,並按照穩定的速度轉動 。轉動速度根據型號不同從 3000 到 1000RPM (轉 / 每分鐘)。磁碟的讀 / 寫磁頭負責讀寫資料,每一個磁碟有一對,每一面一個。讀 / 寫磁頭和磁碟表面並沒有物理的接觸,而是在一個很薄的空氣墊(十萬分之一英寸)上面漂浮。讀寫磁頭通過一個磁碟機在磁碟表面移動。所有的磁頭都粘在一起,一起在磁碟表面移動。 每一個磁碟的表面都分成多個狹窄的同心環,叫做磁軌( track )。磁軌 0 是最外面的磁軌,最高編號的磁軌是最接近中心軸的磁軌。一個柱面( cylinder )是相同編號磁軌的組合。所以每一個磁碟的每一面的所有的第 5 磁軌就是第 5 柱面。因為柱面數和磁軌數相同,所以磁碟的尺寸常用柱面來描述。每一個磁軌分成扇區。一個扇區是可以從硬碟讀寫的最小資料單元,也就是磁碟的塊大小。通常扇區大小是 512 位元組,扇區大小通常是在製造磁碟的時候進行格式化的時候設定的。 磁碟通常用它的尺寸( geometry )描述:柱面數、磁頭數和扇區數。例如,啟動的時候 Linux 這樣描述我的 IDE 磁碟: hdb: Conner Peripherals 540MB - CFS540A, 516MB w/64kB Cache, CHS=1050/16/63 這意味著它由 1050 柱面(磁軌), 16 頭( 8 個磁碟)和 63 個扇區 / 磁軌。對於 512 位元組的扇區或塊大小,磁碟的容量是 529200K 位元組。這和磁碟聲明的 516M 的儲存能力不符合,因為一些扇區用作儲存磁碟的分區資訊。一些磁碟可以自動找出壞的扇區,對其進行重新索引。 硬碟可以再分為分區。一個分區是分配用於特定目的的一大組扇區。對磁碟分割允許磁碟用於幾個作業系統或多個目的。大多數單個磁碟的 Linux 系統都由 3 個分區:一個包含 DOS 檔案系統,另一個是 EXT2 檔案系統,第三個是交換分區。硬碟的分區用分區表描述,每一個條目用磁頭、扇區和柱面號描述分區的起止位置。對於用 fdisk 格式化的 DOS 磁碟,可以有 4 個主要磁碟分區。不是分區表所有的 4 個條目都必須用到。 Fdisk 支援三種類型的分區:主要磁碟分割、擴充分區和邏輯分區。擴充分區不是真正的分區,它可以包括任意數目的邏輯分區。發明擴充分區和邏輯分區是為了突破 4 個主要磁碟分割的限制。下面是一個包括 2 個主要磁碟分割的磁碟的 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 ,扇區 32 和磁頭 63 。因為一個磁軌由 32 個扇區和 64 個讀寫磁頭,這個分區的柱面都是完全包括的。 Fdisk 預設把分區對齊在柱面的邊界。它從最外面的柱面( 0 )開始向內,朝向中心軸,擴充 478 個柱面。第 2 個分區,交換分區,開始於下一個柱面( 478 )並擴充到磁碟最裡面的柱面。 在初始化的時候 Linux 映射系統中的硬碟的拓撲結構。它找出系統中有多少個硬碟以及硬碟的類型。 Linux 還找出每一個磁碟如何分區。這些都是由 gendisk_head 指標 列表指向的一組 gendisk 資料結構的列表表達。對於每一個磁碟子系統,例如 IDE ,初始化的時候產生 gendisk 資料結構表示它找到的磁碟。這個過程和它登記它的檔案操作和在 blk_dev 資料結構中增加它的條目發生在同一時間。每一個 gendisk 資料結構都由一個唯一的主裝置號,和塊特殊裝置的相同。例如, SCSI 磁碟子系統會建立一個獨立的 gendisk 條目(“ sd ”),主裝置號是 8 (所有 SCSI 磁碟裝置的主裝置號)。圖 8.3 顯示了兩個 gendisk 條目,第一個是 SCSI 磁碟子系統,第二個是 IDE 磁碟控制卡。這裡是 ide0 ,主 IDE 控制器。 雖然磁碟子系統在初始化的時候會建立相應的 gendisk 條目, Linux 只是在進行分區檢查的時候才用到。每一個磁碟子系統必須維護自己的資料結構,讓它自己可以把裝置的主裝置號和次裝置號映射到物理磁碟的分區上。不管什麼時候讀寫塊裝置,不管是通過 buffer cache 或者檔案操作,核心都根據它在塊特殊裝置檔案(例如 /dev/sd a2 )中找到的主裝置號和次裝置號把操作定向到合適的裝置。是每一個裝置驅動程式或子系統把次裝置號映射到真正的物理裝置上。 8.5.1 IDE Disks ( IDE 磁碟) 今天 Linux 系統中最常用的磁碟是 IDE 磁碟( Integrated Disk Electronic )。 IDE 和 SCSI 一樣是一個磁碟介面而不是一個 I/O 匯流排。每一個 IDE 控制器可以支援最多 2 個磁碟,一個是 master ,另一個是 slave 。 Master 和 slave 通常用磁碟上的跳線設定。系統中的第一個 IDE 控制器叫做主 IDE 控制器,下一個叫從屬控制器等等。 IDE 可以從 / 向磁碟進行 3.3M/ 秒的傳輸, IDE 磁碟的最大尺寸是 538M 位元組。擴充 IDE 或 EIDE 把最大磁碟尺寸增加到 8.6G 位元組,資料轉送速率高達 16.6M/ 秒。 IDE 和 EIDE 磁碟比 SCSI 磁碟便宜,大多數現代 PC 都有一個或更多的主板上的 IDE 控制器。 Linux 按照它發現的控制器的順序命名 IDE 磁碟。主控制器上的主要磁碟是 /dev/h ad , slave 磁碟是 /dev/hdb 。 /dev/hdc 是次 IDE 控制器上的 master 磁碟。 ID E 子系統向 Linux 登記 IDE 控制器而不是磁碟。主 IDE 控制器的主標識符是 3 ,次 IDE 控制器的標識符是 22 。這意味著如果一個系統有兩個 IDE 控制器,那麼在 blk _dev 和 blkdevs 向量表中在索引 3 和 22 會有 IDE 子系統的條目。 IDE 磁碟的塊特殊檔案反映了這種編號:磁碟 /dev/had 和 /dev/hdb ,都串連在主 IDE 控制器上,主裝置號都是 3 。核心使用主裝置標識符作為索引,對於這些塊特殊檔案的 IDE 子系統進行的所有的檔案或者 buffer cache 操作都被定向到相應的 IDE 子系統。當執行一個請求的時候, IDE 子系統負責判斷這個請求是針對哪一個 IDE 磁碟。為此, IDE 子系統使用裝置特殊檔案中的次裝置號,這些資訊允許它把請求定向到正確的磁碟的正確的分區。 /dev/hdb ,主 IDE 控制器上的 slave IDE 磁碟的裝置標識符是( 3 , 64 )。它的第一個分區( /dev/hdb1 )的裝置標識符是( 3 , 65 )。 8.5.2 Initializing the IDE Subsystem (初始化 IDE 子系統) IBM PC 的大部分曆史中都有 IDE 磁碟。這期間這些裝置的介面發生了變化。這讓 IDE 子系統的初始化過程比它第一次出現的時候更加複雜。 Linux 可以支援的最大 IDE 控制器數目是 4 。每一個控制器都用一個 ide_hwifs 向量表中的一個 ide_hwif_t 資料結構表示。每一個 ide_hwif_t 資料結構包含兩個 ide_drive_t 資料結構,分別表示可能支援的 master 和 slave IDE 磁碟機。在 IDE 子系統初始化期間, Linux 首先查看在系統的 CMOS 記憶體中記錄的磁碟的資訊。這種用電池做後備的記憶體在 PC 關機的時候不會丟失它的內容。這個 CMOS 記憶體實際上在系統的系統時鐘裝置裡面,不管你的 PC 開或者關,它都在運行。 CMOS 記憶體的位置由系統的 BIOS 設定,同時告訴 Linux 系統中找到了什麼 IDE 控制器和磁碟機。 Linux 從 BIOS 中擷取找到的磁碟的尺寸( geometry ),用這些資訊設定這個磁碟機的 ide_hw if_t 的資料結構。大多數現代 PC 使用 PCI 晶片集例如 Intel 的 82430 VX 晶片集,包括了一個 PCI EIDE 控制器。 IDE 子系統使用 PCI BIOS 回調( callback )定位系統中的 PCI ( E ) IDE 控制器。然後調用這些晶片集的詢問常式。 一旦發現一個 IDE 介面或者控制器,就設定它的 ide_hwif_t 來反映這個控制器和上面的磁碟。操作過程中 IDE 驅動程式向 I/O 記憶體空間的 IDE 命令寄存器寫命令。主 IDE 控制器的控制和狀態寄存器的預設的 I/O 地址是 0x1F0-0x1F7 。這些地址是早期的 IBM PC 約定下來的。 IDE 驅動程式向 Linux 的 buffer cache 和 VFS 登記每一個控制器,分別把它加到 blk_dev 和 blkdevs 向量表中。 IDE 驅動程式也請求控制適當的中斷。同樣,這些中斷也有約定,主 IDE 控制器是 14 ,次 IDE 控制器是 15 。但是,象所有的 IDE 細節一樣,這些都可以用核心的命令列選項改變。 IDE 驅動程式在啟動的時候也為每一個找到的 IDE 控制器在 gendisk 列表中增加一個 gendisk 條目。這個列表稍後用於查看啟動時找到的所有的硬碟的分區表。分區檢查代碼明白每一個 I DE 控制器可以控制兩個 IDE 磁碟。 8.5.3 SCSI Disks ( SCSI 磁碟) SCSI ( Small Computer System Interface 小型電腦系統介面)匯流排是一種有效點對點的資料匯流排,每個匯流排支援多達 8 個裝置,每個主機可以有一或者多個。每一個裝置都必須由一個唯一的標識符,通常用磁碟上的跳線設定。資料可以在匯流排上的任意兩個裝置之間同步或者非同步傳輸,可以用 32 位寬的資料轉送,速度可能高達 40M / 秒。 SCSI 匯流排可以在裝置之間傳輸資料和狀態資訊,發起者( initiator )和目標( target )之間的事務會涉及多達 8 個不同的階段。你可以通過 SCSI 匯流排上的 5 種訊號判斷出當前的階段。這 8 個階段是: BUS FREE 沒有裝置有匯流排的控制權,當前沒有發生任何事務。 ARBITRATION (仲裁)一個 SCSI 裝置試圖得到 SCSI 匯流排的控制權,它在地址管腳上聲明( assert )它的 SCSI 標識符。最高編號的 SCSI 標識符成功。 SELECTION 一個裝置通過仲裁成功地得到了 SCSI 匯流排的控制權,現在它必須向它要發送命令的 SCSI 目標發送訊號。它在地址管腳上聲明目標的 SCSI 標識符。 RESELECTION SCSI 裝置在處理請求的過程中可能斷線,目標會重新選擇發起者。並非所有的 SCSI 裝置都支援這一階段。 COMMAND 6 、 10 或者 12 位元組的命令可以從發起者發送到目標。 DATA IN , DATA OUT 在這一階段,資料在發起者和目標之間傳輸。 STATUS 在完成了所有的命令,進入這一階段。允許目標向發起者發送一個狀態位元組,表示成功或失敗。 MESSAGE IN , MESSAGE OUT 在發起者和目標之間傳遞的附加資訊。 Linux SCSI 子系統由兩個基本元素組成,每一個都用資料結構表示: Host 一個 SCSI host 是一個物理的硬體,一個 SCSI 控制器。 NCR810 PCI SCSI 控制器是一個 SCSI host 的例子。如果一個 Linux 系統有多於一個同類型的 SCSI 控制器,每一個執行個體都分別用一個 SCSI host 表示。這意味著一個 SCSI 裝置驅動程式可能控制多於一個控制器的執行個體。 SCSI host 通常總是 SCSI 命令的發起者( initiato r )。 Device SCSI 裝置通常是磁碟,但是 SCSI 標準支援多種類型:磁帶、 CD-ROM 和通用( generic ) SCSI 裝置。 SCSI 裝置通常都是 SCSI 命令的目標。這些裝置必須不同地對待。例如可移動介質如 CD-ROM 或磁帶, Linux 需要探測介質是否取出。不同的磁碟類型有不同的主裝置編號,允許 Linux 把塊裝置請求定向到合適的 SCSI 類型。 Initializing the SCSI Subsystem (初始化 SCSI 子系統) 初始化 SCSI 子系統相當複雜,反映出 SCSI 匯流排和裝置的動態實質。 Linux 在啟動的時候初始化 SCSI 子系統:它尋找系統中的 SCSI 控制器( SCSI host ),並探測每一個 SCSI 匯流排,尋找每一個裝置。然後初始化這些裝置,讓 Linux 核心的其餘部分可以通過普通的檔案和 buffer cache 塊裝置操作訪問它們。這個初始化過程有四個階段:首先, Linux 找出核心建立的時候建立到核心的哪一個 SCSI host 適配器或控制器有可以控制的硬體。每一個內建的 SCSI host 在 buildin_scsi_hosts 向量表中都有一個 Scsi_Host_Template 的條目。這個 Scsi_Host_Template 資料結構包括常式的指標,這些常式可以執行和 SCSI host 相關的動作例如探測這個 SCSI host 上黏附了什麼 S CSI 裝置。這些常式在 SCSI 子系統配置期間被調用,是支援這種 host 類型的 SCSI 裝置驅動程式的一部分。每一個查到的 SCSI 控制器(有真實的 SCSI 裝置黏附),它的 Scsi_Host_Template 資料結構都加到 scsi_hosts 列表中,表示有效 SCSI host 。每一個探測到的 host 類型的每一個執行個體都用 scsi_hostlist 列表中的一個 Scsi_ Host 資料結構表示。例如一個系統有兩個 NCR810 PCI SCSI 控制器,在這個列表中會有兩個 Scsi_Host 條目,每一個控制器一個。每一個 Scsi_Host 指向的 Scsi_Host_T emplate 表示它的裝置驅動程式。 現在每一個 SCSI host 都找到了, SCSI 子系統必須找到每一個 host 匯流排上的所有的 SCSI 裝置。 SCSI 裝置編號從 0 到 7 ,每一個裝置編號或者 SCSI 標識符在它所黏附的 SCSI 匯流排上都是唯一的。 SCSI 標識符通常用裝置上的跳線設定。 SCSI 初始化 代碼通過向每一個裝置發送 TEST_UNIT_READY 命令來尋找一個 SCSI 匯流排上的每一個 SCSI 裝置。當一個裝置回應,再向它發送一個 ENQUIRY 命令來完成它的判別。這向 L inux 給出 Vendor 的名稱和裝置的型號和修訂編號。 SCSI 命令用一個 Scsi_Cmnd 資料結構來表示,這些命令通過調用這個 SCSI host 的 Scsi_Host_Template 資料結構中的裝置驅動程式常式傳遞給裝置驅動程式。每一個找到的 SCSI 裝置用一個 Scsi_Device 資料結構表示,每一個都指向它的父 Scsi_Host 。所有的 Scsi_Device 資料結構都加到 scsi_devices 列表中。圖 8.4 顯示了主要的資料結構和其他資料結構的關係。 有四種 SCSI 裝置類型:磁碟、磁帶、 CD 和通用( generic )。每一種 SCSI 類型都分別向核心登記,有不同的主塊裝置類型。但是,它們只有在一個或多個給定的 SCSI 裝置類型的裝置找到的時候才登記自己。每一個 SCSI 類型,例如 SCSI 磁碟,維護它自己的裝置表。它用這些表把核心的塊操作(檔案或 buffer cache )定向到正確的裝置驅動程式或 SCSI host 。每一個 SCSI 類型都用一個 Scsi_Type_Template 資料結構表示。它包括這種類型的 SCSI 裝置的資訊和執行多種任務的常式的地址。 SCSI 子系統使用這些模板調用每一種 SCSI 裝置類型的 SCSI 類型常式。換句話說,如果 SCSI 子系統希望黏附一個 SCSI 磁碟裝置,它會調用 SCSI 磁碟類型的常式。如果探測到某類型的一個或多個 SCSI 裝置,它的 Scsi_Type_Templates 的資料結構就加到了 scsi _devicelist 列表中。  SCSI 子系統初始化的最後階段是調用每一個登記的 Scsi_Device_Template 的完成函數。對於 SCSI 磁碟類型讓所有的 SCSI 磁碟轉動起來並記錄它們的磁碟尺寸。它也把表示所有 SCSI 磁碟的 gendisk 資料結構增腳的磁碟的連結清單中, 8.3 。   Delivering Block Device Requests (傳遞塊裝置請求) 一旦 Linux 初始化了 SCSI 子系統,就可以使用 SCSI 裝置了。每一個有效 SCSI 裝置類型都在核心中登記自己,所以 Linux 可以把塊裝置請求定向到它那裡。這些請求可能是通過 blk_dev 的 buffer cache 請求或者是通過 blkdevs 的檔案操作。拿一個由一個或多個 EXT2 檔案系統分區的 SCSI 磁碟機為例,當它的 EXT2 分區安裝上的時候核心的緩衝區請求是如何定向到正確的 SCSI 磁碟呢? 每一個向 / 從一個 SCSI 磁碟分割讀 / 寫一塊資料的請求都會在 blk_dev 向量表中這個 SCSI 磁碟的 current_request 列表中加入一個新的 request 資料結構。如果這個 request 列表正在處理,那麼 buffer cache 不需要做什麼。否則它必須讓 SCSI 磁碟子系統處理它的請求隊列。系統中的每一個 SCSI 磁碟用一個 Scsi_Disk 資料結構表示。它們儲存在 rscsi_disks 向量表中,用 SCSI 磁碟分割的次裝置號的一部分作為索引。例如, /dev/sdb1 主裝置號 8 ,次裝置號 17 ,它的所以是 1 。每一個 Scsi_Dis k 的資料結構包括一個指向表示這個裝置的 Scsi_Device 資料結構的指標。 Scsi_Dev ice 又指向一個“擁有它”的 Scsi_Host 資料結構。 Buffer cache 中的 request 資料結構轉換成為描述需要發送到 SCSI 裝置的 SCSI 命令的 Scsi_Cmd 資料結構中,並在表示這個裝置的 Scsi_Host 資料結構中排隊。一旦適當的資料區塊讀 / 寫之後,會由各自的 SCSI 裝置驅動程式處理。  8.6 Network Devices (網路裝置)  一個網路裝置,只要關係到 Linux 的網路子系統,是一個發送和接收資料包的實體。通常是一個物理的裝置,例如一個乙太網路卡。但是一些網路裝置是純軟體的,例如 loopb ack 裝置,用於向自己發送資料。每一個網路裝置用一個 device 資料結構表示。網路裝置驅動程式在核心啟動網路初始化的時候向 Linux 登記它控制的裝置。 Device 資料結構包括這個裝置的資訊和允許大量支援的網路通訊協定使用這個裝置的服務的函數的地址。這些函數多數和使用這個網路裝置傳輸資料有關。裝置使用標準的網路支援機制,向適當的協議層傳輸接收的資料。傳輸和接收的所有的網路資料(包 packets )都用 sk _buff 資料結構表示,這是靈活的資料結構,允許網路通訊協定頭很容易地增加和刪除。網路通訊協定層如何使用網路裝置,它們如何使用 sk_buff 資料結構來回傳遞資料,在網路章(第 10 章)有詳細的描述。本章集中在 device 資料結構以及網路裝置如何被發現和初始化。參見 include/linux/netdevice.h   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 介面 up ,正在運行 IFF_BROADCAST 裝置的廣播位址有效 IFF_DEBUG 裝置的 debug 選項開啟 IFF_LOOPBACK 這是一個 loopback 裝置 IFF_POINTTOPOINT 這是點對點的串連( SLIP and PPP ) IFF_NOTRAILERS No network trailers IFF_RUNNING 分配了資源 IFF_NOARP 不支援 ARP 協議 IF_PROMISC 裝置在混合( promiscuous )接收模式,它會接收所有的包,不管它們的地址是誰。 IFF_ALLMULTI 接收所有的 IP Multicast 幀 IFF_MULTICAST 可以接收 IP multicast 幀  Protocal Information 每一個裝置都描述它可以被網路通訊協定層如何使用: Mtu 不包括需要增加的鏈路層的頭這個網路能夠傳輸的最大尺寸的包。這個最大值用於協議層例如 IP ,來選擇一個合適的包大小進行發送。 Family family 顯示了裝置可以支援的協議族。所有 Linux 網路裝置都支援的 family 是 AF_INET , 網際網路位址 family 。 Type 硬體介面類型描述了這個網路裝置串連的介質。 Linux 網路裝置支援多種介質類型。包括 Ethernet 、 X.25 , Token Ring 、 Slip 、 PPP 和 Apple Localtalk 。 Addresses device 資料結構儲存一些和這個網路裝置相關的地址,包括 IP 位址  Packet Queue 這是一個 sk_buff 的包隊列,等待網路裝置進行傳輸  Support Functions 每一個裝置都提供了一組標準的常式,讓協議層調用,作為對於裝置鏈路層的介面的一部分。包括設定和幀傳輸常式,以及增加標準幀頭和收集統計資訊的常式。這些統計資訊可以用 ifcnfig 看到  8.6.1 Initializing Network Devices (初始化網路裝置) 網路裝置驅動程式象其他 Linux 裝置驅動程式一樣,可以建立到 Linux 核心中。每一個可能的網路裝置都用 dev_base 列表指標指向的網路裝置列表中的一個 device 資料結構表示。如果需要裝置相關的操作,網路層調用網路裝置服務常式(放在 device 資料結構中)其中的一個。但是,初始的時候,每一個 device 資料結構只是放了初始化 或者探測常式的地址。 網路驅動程式必須解決兩個問題。首先,不是所有建立在 Linux 核心的網路裝置驅動程式都會有控制的裝置;第二,系統中的乙太網路裝置總是叫做 /dev/eth0 、 /dev/eth1 等等,而不管底層的裝置驅動程式是什麼。“丟失“網路裝置的問題容易解決。在調用每一個網路裝置的初始化常式的時候,它返回一個狀態,顯示它是否定位到了它驅動的控制器的一個執行個體。如果驅動程式沒有找到任何裝置,它由 dev_base 指向的 device 列表中的條目就被刪除。如果驅動程式可以找到一個裝置,它就用這個裝置的資訊和網路裝置驅動程式中的支援函數的地址填充 device 資料結構其餘的部分。 第二個問題,就是動態地分配乙太網路裝置到標準的 /dev/ethN 裝置特殊檔案上,用更優雅的方式解決。 Device 列表中有 8 個標準的條目: eth0 、 eth1 到 eth7 。所有條目的初始化常式都一樣。它順序嘗試建立在核心的每一個乙太網路裝置驅動程式,直到找到一個裝置。當驅動程式找到它的乙太網路裝置,它就填充它現在擁有的 ethN 的 devic e 資料結構。這時網路驅動程式也要初始化它控制的物理硬體,並找出它使用的 IRQ 、 DMA 等等。驅動程式可能找到它控制的網路裝置的幾個執行個體,在這種情況下,它就佔用幾個 /dev/ethN 的 device 資料結構。一旦所有的 8 個標準的 /dev/ethN 都分配了,就不會再探測更多的乙太網路裝置。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.