|
十一、Linux驅(qū)動(dòng)程序開發(fā)(1) - 設(shè)備與驅(qū)動(dòng)的關(guān)系以及設(shè)備號(hào)、設(shè)備文件
這章將介紹Linux系統(tǒng)的設(shè)
備,這樣我們才能清楚的知道應(yīng)用程序和設(shè)備驅(qū)動(dòng)程序是如何的工作的,或者說應(yīng)用程序是如何控制驅(qū)動(dòng)程序的,進(jìn)而知道應(yīng)用程序是如何通過驅(qū)動(dòng)程序操作設(shè)備
的,另外會(huì)詳細(xì)的介紹設(shè)備號(hào)及設(shè)備文件。 Linux設(shè)備分類 字符設(shè)備 一個(gè)字符設(shè)
備是一種字節(jié)流設(shè)備,對(duì)設(shè)備的存取只能按順序按字節(jié)的存取而不能隨機(jī)訪問,字符設(shè)備沒有請(qǐng)求緩沖區(qū),所有的訪問請(qǐng)求都是按順序執(zhí)行的。Linux下的大多設(shè)備都是字符設(shè)備。應(yīng)用程序是通過字符設(shè)備節(jié)點(diǎn)來訪問
字符設(shè)備的。設(shè)備節(jié)點(diǎn)一般都由mknod命令都創(chuàng)
建在/dev目錄下,下
面的例子顯示了串口設(shè)備的設(shè)備節(jié)點(diǎn)。字符設(shè)備文件的第一個(gè)標(biāo)志是前面的“c”標(biāo)志。
字符設(shè)備是指那些只能按順序一個(gè)字節(jié)一個(gè)字節(jié)讀取的設(shè)備,但事實(shí)上現(xiàn)在一些高級(jí)
字符設(shè)備也可以從指定位置一次讀取一塊數(shù)據(jù)。字符設(shè)備是面向數(shù)據(jù)流的設(shè)備,每個(gè)字符設(shè)備都有一個(gè)設(shè)備號(hào),設(shè)備號(hào)由主設(shè)備號(hào)和次設(shè)備號(hào)組成。同時(shí)Linux使用管理文件相同的方法來管理字符設(shè)備,所以每個(gè)字符設(shè)備在/dev/目錄下都有一個(gè)對(duì)應(yīng)的設(shè)備文件,即設(shè)備節(jié)點(diǎn),它們包含了設(shè)備的
類型、主/次設(shè)備號(hào)以
及設(shè)備的訪問權(quán)限控制等,系統(tǒng)通過設(shè)備文件來對(duì)字符設(shè)備進(jìn)行操作,每個(gè)字符設(shè)備文件都有自己的與普通文件相同的文件操作函數(shù)組結(jié)構(gòu)(struct file_operations)。字符設(shè)
備驅(qū)動(dòng)通常至少需要實(shí)現(xiàn)文件操作函數(shù)組中的open、release、read和write四種操作方法。常見的字符設(shè)備有鼠標(biāo)、鍵盤、串口、控制臺(tái)等。 塊設(shè)備 存儲(chǔ)設(shè)備一
般屬于塊設(shè)備,塊設(shè)備有請(qǐng)求緩沖區(qū),并且支持隨機(jī)訪問而不必按照順序去存取數(shù)據(jù),比如你可以
先存取后面的數(shù)據(jù),然后在存取前面的數(shù)據(jù),這對(duì)字符設(shè)備來說是不可能的。Linux下的磁盤
設(shè)備都是塊設(shè)備,盡管在Linux下有塊設(shè)
備節(jié)點(diǎn),但應(yīng)用程序一般是通過文件系統(tǒng)及其高速緩存來訪問塊設(shè)備的,而不是直
接通過設(shè)備節(jié)點(diǎn)來讀寫塊設(shè)備上的數(shù)據(jù)。塊設(shè)備文件的第一個(gè)標(biāo)志是前面的“b”標(biāo)志。
塊設(shè)備是指那些可以從設(shè)備的任意位置讀取任意長(zhǎng)度數(shù)據(jù)的設(shè)備。每個(gè)塊設(shè)備同樣有
一個(gè)設(shè)備號(hào),設(shè)備號(hào)由主設(shè)備號(hào)和次設(shè)備號(hào)組成。同時(shí)Linux也使用管
理文件相同的方法來管理塊設(shè)備,每個(gè)塊設(shè)備在/dev/目錄下都
有一個(gè)對(duì)應(yīng)的設(shè)備文件,即設(shè)備節(jié)點(diǎn),它們包含了設(shè)備的類型、主/次設(shè)備號(hào)
以及設(shè)備的訪問權(quán)限控制等,系統(tǒng)通過設(shè)備文件來對(duì)塊設(shè)備進(jìn)行操作,每個(gè)塊設(shè)備文件都有自己的與普通文件相同的文件操作函數(shù)組結(jié)構(gòu)(struct file_operations)。但塊設(shè)
備需要實(shí)現(xiàn)的操作方法遠(yuǎn)比字符設(shè)備的操作方法多得多,也難得多。塊設(shè)備既可以作為普通的裸設(shè)備用來存放任意數(shù)據(jù),也可以將塊設(shè)備按某種文件系統(tǒng)類型的格式
進(jìn)行格式化,然后按照該文件系統(tǒng)類型的格式來讀取塊設(shè)備上的數(shù)據(jù),但不管哪種方式,最后訪問設(shè)備上的數(shù)據(jù)都必須通過調(diào)用設(shè)備本身的操作方法實(shí)現(xiàn),區(qū)別在于
前者直接調(diào)用塊設(shè)備的操作方法,而后者則間接調(diào)用塊設(shè)備的操作方法。常見的塊設(shè)備有各種硬盤、flash磁盤、RAM磁盤等。 網(wǎng)絡(luò)設(shè)備 網(wǎng)絡(luò)設(shè)備不
同于字符設(shè)備和塊設(shè)備,它是面向報(bào)文的而不是面向流的,它不支持隨機(jī)訪問,也沒有請(qǐng)求緩沖區(qū)。在Linux里一個(gè)網(wǎng)絡(luò)設(shè)備也可以叫做一個(gè)網(wǎng)絡(luò)接口,如eth0,應(yīng)用程序是通過Socket而不是設(shè)備節(jié)點(diǎn)來訪問網(wǎng)絡(luò)設(shè)備,在系統(tǒng)里根本就不存在網(wǎng)絡(luò)設(shè)備節(jié)點(diǎn)。 網(wǎng)絡(luò)接口用來與其他設(shè)備交換數(shù)據(jù),它可以是硬件設(shè)備,也可以是純軟件設(shè)備,如loopback接口就是一個(gè)純軟件設(shè)備。網(wǎng)絡(luò)接口由內(nèi)核中的網(wǎng)絡(luò)
子系統(tǒng)驅(qū)動(dòng),負(fù)責(zé)發(fā)送和接收數(shù)據(jù)包,但它不需要了解每項(xiàng)事務(wù)如何映射到實(shí)際傳送的數(shù)據(jù)包,許多網(wǎng)絡(luò)連接(尤其是使用TCP協(xié)議的連接)是面向流的,但網(wǎng)絡(luò)設(shè)備圍繞數(shù)據(jù)包的傳輸和接收設(shè)
計(jì)。網(wǎng)絡(luò)驅(qū)動(dòng)程序不需要知道各個(gè)連接的相關(guān)信息,它只需處理數(shù)據(jù)包。網(wǎng)絡(luò)接口沒有像字符設(shè)備和塊設(shè)備一樣的設(shè)備號(hào),只有一個(gè)唯一的名字,如eth0、eth1等,而這個(gè)名字也不需要與設(shè)備文件節(jié)點(diǎn)對(duì)應(yīng)。內(nèi)核使用一套與數(shù)據(jù)
包傳輸相關(guān)的函數(shù)來與網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序通信,它們不同于字符設(shè)備和塊設(shè)備的read()和write()方法。
Linux的設(shè)備管理是和文件系統(tǒng)緊密結(jié)合的,各種設(shè)備都以文件的形式存
放在/dev目錄下,稱
為設(shè)備文件。應(yīng)用程序可以打開、關(guān)閉和讀寫這些設(shè)備文件,完成對(duì)設(shè)備的操作,就像操作普通的數(shù)據(jù)文件一樣。為了管理這些設(shè)備,系統(tǒng)為設(shè)備編了號(hào),每個(gè)設(shè)備
號(hào)又分為主設(shè)備號(hào)和次設(shè)備號(hào)。主設(shè)備號(hào)用來區(qū)分不同種類的設(shè)備,而次設(shè)備號(hào)用來區(qū)分同一類型的多個(gè)設(shè)備。對(duì)于常用設(shè)備,Linux有約定俗成的編號(hào),如硬盤的主設(shè)備號(hào)是3。 Linux為所有的
設(shè)備文件都提供了統(tǒng)一的操作函數(shù)接口,方法是使用數(shù)據(jù)結(jié)構(gòu)struct
file_operations。這個(gè)數(shù)據(jù)結(jié)構(gòu)中包括許多操作函數(shù)的指針,如open()、close()、read()和write()等,但由于外設(shè)的種類較多,操作方式各不相同。Struct file_operations結(jié)構(gòu)體中的
成員為一系列的接口函數(shù),如用于讀/寫的read/write函數(shù)和用于控制的ioctl等。打開一個(gè)文件就是調(diào)用這個(gè)文件file_operations中的open操作。不同類型的文件有不同的file_operations成員函數(shù),
如普通的磁盤數(shù)據(jù)文件,接口函數(shù)完成磁盤數(shù)據(jù)塊讀寫操作;而對(duì)于各種設(shè)備文件,則最終調(diào)用各自驅(qū)動(dòng)程序中的I/O函數(shù)進(jìn)行具體設(shè)備的操作。這樣,應(yīng)用程序根本不必考慮操作的是設(shè)
備還是普通文件,可一律當(dāng)作文件處理,具有非常清晰統(tǒng)一的I/O接口。所
以file_operations是文件層
次的I/O接口。 主設(shè)備號(hào) 驅(qū)動(dòng)程序在
初始化時(shí),會(huì)注冊(cè)它的驅(qū)動(dòng)及對(duì)應(yīng)主設(shè)備號(hào)到系統(tǒng)中,這樣當(dāng)應(yīng)用程序訪問設(shè)備節(jié)點(diǎn)時(shí),系統(tǒng)就知道它所訪問的驅(qū)動(dòng)程序了。你可以通過/proc/devices文件來驅(qū)動(dòng)
系統(tǒng)設(shè)備的主設(shè)備號(hào)。 次設(shè)備號(hào) 驅(qū)動(dòng)程序遍
歷設(shè)備時(shí),每發(fā)現(xiàn)一個(gè)它能驅(qū)動(dòng)的設(shè)備,就創(chuàng)建一個(gè)設(shè)備對(duì)象,并為其分配一個(gè)次設(shè)備號(hào)以區(qū)分不同的設(shè)備。這樣當(dāng)應(yīng)用程序訪問設(shè)備節(jié)點(diǎn)時(shí)驅(qū)動(dòng)程序就可以根據(jù)次
設(shè)備號(hào)知道它說訪問的設(shè)備了。 系統(tǒng)中的每一個(gè)字符設(shè)備和塊設(shè)備(網(wǎng)絡(luò)接口沒有設(shè)備號(hào))都有一個(gè)設(shè)備號(hào),傳統(tǒng)的UNIX以及早期版本Linux中的設(shè)備號(hào)是16位的,主次設(shè)備號(hào)都是8位的,低8位為次設(shè)備號(hào),高8位為主設(shè)備號(hào),因此系統(tǒng)最多分別支持65536個(gè)字符設(shè)備和65536個(gè)塊設(shè)備,這個(gè)限制已經(jīng)不能滿足當(dāng)
前層出不窮的各種新設(shè)備的需要,所以Linux2.6中對(duì)設(shè)備號(hào)已經(jīng)進(jìn)行了擴(kuò)展,一個(gè)設(shè)備
號(hào)為32位,主設(shè)備號(hào)為12位,次設(shè)備號(hào)為20位,但是這32位設(shè)備號(hào)的編碼方式有新舊兩種,舊的
設(shè)備編號(hào)格式為:最高12位為主設(shè)備號(hào),最低20位為次設(shè)備號(hào);新的設(shè)備編號(hào)格式為:bit[19:8]是主設(shè)備號(hào),bit[31:20]是次設(shè)備號(hào)的高12位,bit[7:0]是次設(shè)備號(hào)的低8位。如果知道了一個(gè)設(shè)備的主設(shè)備號(hào)major和次設(shè)備號(hào)minor,那么用MKDEV(major,minor)生成是該設(shè)備的舊格式的設(shè)備號(hào),用new_encode_dev(MKDEV(major,minor))生成的則是新格式的設(shè)備號(hào)。Linux支持的各種設(shè)備的主設(shè)備號(hào)定義在include/linux/major.h文件中,而已經(jīng)在官方注冊(cè)的主設(shè)備
號(hào)和次設(shè)備號(hào)在Documentation/devices.txt文件中可以找到。 老式16位設(shè)備
號(hào)、32位舊格式設(shè)
備號(hào)以及32位新格式設(shè)
備號(hào)的轉(zhuǎn)換操作函數(shù)如下: new_encode_dev(dev_t dev)函數(shù) 將32位舊格式
設(shè)備號(hào)dev轉(zhuǎn)換成32位新格式設(shè)備號(hào)。 new_decode_dev(u32 dev)函數(shù) 將32位新格式
設(shè)備號(hào)轉(zhuǎn)換成32位舊格式設(shè)
備號(hào)。 old_encode_dev(dev_t dev)函數(shù) 將32位舊格式
設(shè)備號(hào)轉(zhuǎn)換成老式16位設(shè)備號(hào)。 dev_t old_decode_dev(u16 val)函數(shù) 將老式16位設(shè)備號(hào)轉(zhuǎn)換成32位舊格式設(shè)備號(hào)。 Linux中設(shè)備節(jié)點(diǎn)是通過“mknod”命令來創(chuàng)建
的。一個(gè)設(shè)備節(jié)點(diǎn)其實(shí)就是一個(gè)文件,Linux中稱為設(shè)
備文件。有一點(diǎn)必要說明的是,在Linux中,所有
的設(shè)備訪問都是通過文件的方式,一般的數(shù)據(jù)文件程序普通文件,設(shè)備節(jié)點(diǎn)稱為設(shè)備文件。在Linux內(nèi)核中網(wǎng)絡(luò)設(shè)備也是通過文件操作的,稱為網(wǎng)絡(luò)設(shè)備文件,在用戶空間是通過socket接口來訪問的。socket號(hào)就是網(wǎng)絡(luò)設(shè)備文件描述符。 如:mknod /dev/mydevice c
254 0 (c代表子都設(shè)備,254為主設(shè)備號(hào),0為次設(shè)備號(hào)) Open,close等操作/dev/下設(shè)備文件,內(nèi)核根據(jù)文件的主設(shè)備號(hào)找到對(duì)應(yīng)的設(shè)備驅(qū)動(dòng) 主設(shè)備號(hào)可以分為動(dòng)態(tài)和靜態(tài)申請(qǐng)。 設(shè)備文件 |
|
|