?
11.1??設(shè)備驅(qū)動概述
11.1.1??設(shè)備驅(qū)動簡介及驅(qū)動模塊
操作系統(tǒng)是通過各種驅(qū)動程序來駕馭硬件設(shè)備的,它為用戶屏蔽了各種各樣的設(shè)備,驅(qū)動硬件是操作系統(tǒng)最基本的功能,并且提供統(tǒng)一的操作方式。設(shè)備驅(qū)動程序是內(nèi)核的一部分,硬件驅(qū)動程序是操作系統(tǒng)最基本的組成部分,在Linux內(nèi)核源程序中也占有60%以上。因此,熟悉驅(qū)動的編寫是很重要的。
在第2章中已經(jīng)提到過,Linux內(nèi)核中采用可加載的模塊化設(shè)計(LKMs,Loadable?Kernel?Modules),一般情況下編譯的Linux內(nèi)核是支持可插入式模塊的,也就是將最基本的核心代碼編譯在內(nèi)核中,其他的代碼可以編譯到內(nèi)核中,或者編譯為內(nèi)核的模塊文件(在需要時動態(tài)加載)。
常見的驅(qū)動程序是作為內(nèi)核模塊動態(tài)加載的,比如聲卡驅(qū)動和網(wǎng)卡驅(qū)動等,而Linux最基礎(chǔ)的驅(qū)動,如CPU、PCI總線、TCP/IP協(xié)議、APM(高級電源管理)、VFS等驅(qū)動程序則直接編譯在內(nèi)核文件中。有時也把內(nèi)核模塊叫做驅(qū)動程序,只不過驅(qū)動的內(nèi)容不一定是硬件罷了,比如ext3文件系統(tǒng)的驅(qū)動。因此,加載驅(qū)動就是加載內(nèi)核模塊。
這里,首先列舉一些模塊相關(guān)的命令。
n lsmod列出當(dāng)前系統(tǒng)中加載的模塊,其中左邊第一列是模塊名,第二列是該模塊大小,第三列則是使用該模塊的對象數(shù)目。如下所示:
$?lsmod??????
Module???????????????Size????Used?by????
Autofs???????????????12068???0??(autoclean)?(unused)
eepro100????????????18128???1?
iptable_nat ????????19252???0??(autoclean)?(unused)
ip_conntrack???????18540???1??(autoclean)?[iptable_nat]
iptable_mangle?????2272????0??(autoclean)?(unused)
iptable_filter?????2272????0??(autoclean)?(unused)
ip_tables???????????11936???5??[iptable_nat?iptable_mangle?iptable_filter]
usb-ohci????????????19328???0??(unused)
usbcore?????????????54528???1??[usb-ohci]
ext3?????????????????67728???2?
jbd??????????????????44480???2??[ext3]
aic7xxx?????????????114704??3?
sd_mod??????????????11584???3?
scsi_mod???????????98512???2??[aic7xxx?sd_mod]
n rmmod是用于將當(dāng)前模塊卸載。
n insmod和modprobe是用于加載當(dāng)前模塊,但insmod不會自動解決依存關(guān)系,即如果要加載的模塊引用了當(dāng)前內(nèi)核符號表中不存在的符號,則無法加載,也不會去查在其他尚未加載的模塊中是否定義了該符號;modprobe可以根據(jù)模塊間依存關(guān)系以及/etc/modules.conf文件中的內(nèi)容自動加載其他有依賴關(guān)系的模塊。
11.1.2??設(shè)備分類
本書在前面也提到過,Linux的一個重要特點(diǎn)就是將所有的設(shè)備都當(dāng)做文件進(jìn)行處理,這一類特殊文件就是設(shè)備文件,它們可以使用前面提到的文件、I/O相關(guān)函數(shù)進(jìn)行操作,這樣就大大方便了對設(shè)備的處理。它通常在/dev下面存在一個對應(yīng)的邏輯設(shè)備節(jié)點(diǎn),這個節(jié)點(diǎn)以文件的形式存在。
Linux系統(tǒng)的設(shè)備分為3類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備。
n 字符設(shè)備通常指像普通文件或字節(jié)流一樣,以字節(jié)為單位順序讀寫的設(shè)備,?如并口設(shè)備、虛擬控制臺等。字符設(shè)備可以通過設(shè)備文件節(jié)點(diǎn)訪問,它與普通文件之間的區(qū)別在于普通文件可以被隨機(jī)訪問(可以前后移動訪問指針),而大多數(shù)字符設(shè)備只能提供順序訪問,因?yàn)閷λ鼈兊脑L問不會被系統(tǒng)所緩存。但也有例外,例如幀緩存(framebuffer)是一個可以被隨機(jī)訪問的字符設(shè)備。
n 塊設(shè)備通常指一些需要以塊為單位隨機(jī)讀寫的設(shè)備,如IDE硬盤、SCSI硬盤、光驅(qū)等。塊設(shè)備也是通過文件節(jié)點(diǎn)來訪問,它不僅可以提供隨機(jī)訪問,而且可以容納文件系統(tǒng)(例如硬盤、閃存等)。Linux可以使用戶態(tài)程序像訪問字符設(shè)備一樣每次進(jìn)行任意字節(jié)的操作,只是在內(nèi)核態(tài)內(nèi)部中的管理方式和內(nèi)核提供的驅(qū)動接口上不同。
?
通過文件屬性可以查看它們是哪種設(shè)備文件(字符設(shè)備文件或塊設(shè)備文件)。
$?ls?–l?/dev
crw-rw----??1?root??uucp?4,??64?08-30?22:58?ttyS0?/*串口設(shè)備,?c表示字符設(shè)備*/
brw-r-----??1?root??floppy?2,???0?08-30?22:58?fd0/*軟盤設(shè)備,b表示塊設(shè)備*/
n 網(wǎng)絡(luò)設(shè)備通常是指通過網(wǎng)絡(luò)能夠與其他主機(jī)進(jìn)行數(shù)據(jù)通信的設(shè)備,如網(wǎng)卡等。
內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動程序之間的通信調(diào)用一套數(shù)據(jù)包處理函數(shù),它們完全不同于內(nèi)核和字符以及塊設(shè)備驅(qū)動程序之間的通信(read()、write()等函數(shù))。Linux網(wǎng)絡(luò)設(shè)備不是面向流的設(shè)備,因此不會將網(wǎng)絡(luò)設(shè)備的名字(例如eth0)映射到文件系統(tǒng)中去。
對這3種設(shè)備文件編寫驅(qū)動程序時會有一定的區(qū)別,本書在后面會有相關(guān)內(nèi)容的講解。
11.1.3??設(shè)備號
設(shè)備號是一個數(shù)字,它是設(shè)備的標(biāo)志。就如前面所述,一個設(shè)備文件(也就是設(shè)備節(jié)點(diǎn))可以通過mknod命令來創(chuàng)建,其中指定了主設(shè)備號和次設(shè)備號。主設(shè)備號表明設(shè)備的類型(例如串口設(shè)備、SCSI硬盤),與一個確定的驅(qū)動程序?qū)?yīng);次設(shè)備號通常是用于標(biāo)明不同的屬性,例如不同的使用方法、不同的位置、不同的操作等,它標(biāo)志著某個具體的物理設(shè)備。高字節(jié)為主設(shè)備號,底字節(jié)為次設(shè)備號。
例如,在系統(tǒng)中的塊設(shè)備IDE硬盤的主設(shè)備號是3,而多個IDE硬盤及其各個分區(qū)分別賦予次設(shè)備號1、2、3…
$?ls?–l?/dev
crw-rw----??1?root??uucp?4,??64?08-30?22:58?ttyS0?/*?主設(shè)備號4,此設(shè)備號64?*/
11.1.4??驅(qū)動層次結(jié)構(gòu)
Linux下的設(shè)備驅(qū)動程序是內(nèi)核的一部分,運(yùn)行在內(nèi)核模式下,也就是說設(shè)備驅(qū)動程序?yàn)閮?nèi)核提供了一個I/O接口,用戶使用這個接口實(shí)現(xiàn)對設(shè)備的操作。圖11.1顯示了典型的Linux輸入/輸出系統(tǒng)中各層次結(jié)構(gòu)和功能。
圖11.1??Linux輸入/輸出系統(tǒng)
層次結(jié)構(gòu)和功能
Linux設(shè)備驅(qū)動程序包含中斷處理程序和設(shè)備服務(wù)子程序兩部分。
設(shè)備服務(wù)子程序包含了所有與設(shè)備操作相關(guān)的處理代碼。它從面向用戶進(jìn)程的設(shè)備文件系統(tǒng)中接受用戶命令,并對設(shè)備控制器執(zhí)行操作。這樣,設(shè)備驅(qū)動程序屏蔽了設(shè)備的特殊性,使用戶可以像對待文件一樣操作設(shè)備。
設(shè)備控制器獲得系統(tǒng)服務(wù)有兩種方式:查詢和中斷。因?yàn)長inux的設(shè)備驅(qū)動程序是內(nèi)核的一部分,在設(shè)備查詢期間系統(tǒng)不能運(yùn)行其他代碼,查詢方式的工作效率比較低,所以只有少數(shù)設(shè)備如軟盤驅(qū)動程序采取這種方式,大多設(shè)備以中斷方式向設(shè)備驅(qū)動程序發(fā)出輸入/輸出請求。
?
11.1.5??設(shè)備驅(qū)動程序與外界的接口
每種類型的驅(qū)動程序,不管是字符設(shè)備還是塊設(shè)備都為內(nèi)核提供相同的調(diào)用接口,因此內(nèi)核能以相同的方式處理不同的設(shè)備。Linux為每種不同類型的設(shè)備驅(qū)動程序維護(hù)相應(yīng)的數(shù)據(jù)結(jié)構(gòu),以便定義統(tǒng)一的接口并實(shí)現(xiàn)驅(qū)動程序的可裝載性和動態(tài)性。Linux設(shè)備驅(qū)動程序與外界的接口可以分為如下3個部分。
n 驅(qū)動程序與操作系統(tǒng)內(nèi)核的接口:這是通過數(shù)據(jù)結(jié)構(gòu)file_operations(在本書后面會有詳細(xì)介紹)來完成的。
n 驅(qū)動程序與系統(tǒng)引導(dǎo)的接口:這部分利用驅(qū)動程序?qū)υO(shè)備進(jìn)行初始化。
n 驅(qū)動程序與設(shè)備的接口:這部分描述了驅(qū)動程序如何與設(shè)備進(jìn)行交互,這與具體設(shè)備密切相關(guān)。
它們之間的相互關(guān)系如圖11.2所示。
圖11.2??設(shè)備驅(qū)動程序與外界的接口
11.1.6??設(shè)備驅(qū)動程序的特點(diǎn)
綜上所述,Linux中的設(shè)備驅(qū)動程序有如下特點(diǎn)。
(1)內(nèi)核代碼:設(shè)備驅(qū)動程序是內(nèi)核的一部分,如果驅(qū)動程序出錯,則可能導(dǎo)致系統(tǒng)崩潰。
(2)內(nèi)核接口:設(shè)備驅(qū)動程序必須為內(nèi)核或者其子系統(tǒng)提供一個標(biāo)準(zhǔn)接口。比如,一個終端驅(qū)動程序必須為內(nèi)核提供一個文件I/O接口;一個SCSI設(shè)備驅(qū)動程序應(yīng)該為SCSI子系統(tǒng)提供一個SCSI設(shè)備接口,同時SCSI子系統(tǒng)也必須為內(nèi)核提供文件的I/O接口及緩沖區(qū)。
(3)內(nèi)核機(jī)制和服務(wù):設(shè)備驅(qū)動程序使用一些標(biāo)準(zhǔn)的內(nèi)核服務(wù),如內(nèi)存分配等。
(4)可裝載:大多數(shù)的Linux操作系統(tǒng)設(shè)備驅(qū)動程序都可以在需要時裝載進(jìn)內(nèi)核,在不需要時從內(nèi)核中卸載。
(5)可設(shè)置:Linux操作系統(tǒng)設(shè)備驅(qū)動程序可以集成為內(nèi)核的一部分,并可以根據(jù)需要把其中的某一部分集成到內(nèi)核中,這只需要在系統(tǒng)編譯時進(jìn)行相應(yīng)的設(shè)置即可。
(6)動態(tài)性:在系統(tǒng)啟動且各個設(shè)備驅(qū)動程序初始化后,驅(qū)動程序?qū)⒕S護(hù)其控制的設(shè)備。如果該設(shè)備驅(qū)動程序控制的設(shè)備不存在也不影響系統(tǒng)的運(yùn)行,那么此時的設(shè)備驅(qū)動程序只是多占用了一點(diǎn)系統(tǒng)內(nèi)存罷了。