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