linux裝置節點是應用程式和裝置驅動程式溝通的一個橋樑;裝置節點被建立在「/dev」,是連線核心與使用者層的樞紐,相當於硬碟的inode一樣的東西,記錄了硬體裝置的位置和資訊。裝置節點使使用者可以與核心進行硬體的溝通,讀寫裝置以及其他的操作。
本教學操作環境:linux5.9.8系統、Dell G3電腦。
什麼是裝置節點
人和人之間溝通橋樑是語言。同樣,應用程式和裝置驅動程式溝通也需要一個橋樑。這個橋樑就是裝置節點。
對於Linux系統,所有的IO資源都是檔案,包括檔案、目錄、硬碟、裝置等。那麼,鍵盤作為計算機系統中的一款輸入裝置,作業系統同樣也把它抽象了檔案,要想獲取使用者從鍵盤上輸入的資料時,只需要讀取鍵盤提供的裝置節點即可。
在Linux系統中,鍵盤作為輸入裝置,其對應的裝置節點位於」/dev/input「下。在這個資料夾下有很多以event打頭的檔案,這些就是所有input裝置的裝置節點。如何確定哪個是鍵盤的裝置節點呢?將鍵盤連線到樹莓派上,開啟終端,執行「sudo cat /dev/input/event0」,敲擊鍵盤,如果沒有輸出,就換下一個節點,直到找到有輸出的節點,那這個節點就是鍵盤對應的裝置節點。
裝置節點被建立在/dev下,是連線核心與使用者層的樞紐,就是裝置是接到對應哪種介面的哪個ID 上。 相當於硬碟的inode一樣的東西,記錄了硬體裝置的位置和資訊
在Linux中,所有裝置都以檔案的形式存放在/dev目錄下,都是通過檔案的方式進行存取,裝置節點是Linux核心對裝置的抽象,一個裝置節點就是一個檔案。應用程式通過一組標準化的呼叫執行存取裝置,這些呼叫獨立於任何特定的驅動程式。而驅動程式負責將這些標準呼叫對映到實際硬體的特有操作。
裝置節點的作用
裝置節點使得使用者可以與核心進行硬體的溝通,讀寫裝置以及其他的操作
在linux裡面裝置就像是普通檔案一樣的存在,存取一個裝置就好像是存取一個檔案一樣
主裝置號代表著一類裝置,次裝置號代表著同一類裝置的不同個體,說到這裡也許並不知道裝置節點的存在形式
裝置節點的存在形式
另外在linux裡面還有一個概念,就是inode與block,也就是硬碟一面的塊與節點,硬碟裡面的inode就相當於一個檔案或者資料夾,它記錄下此檔案下面的檔案位置所在,檔案的位置是以block大小對齊的,例如有些系統就是4K的大小,而inode的大小是有限的,所以就有了單個檔案不能超過4G的說法。而在linux的驅動程式裡面的節點在我個人的理解也可以看做是一個類似於硬碟的inode一樣的東西,裡面可以記錄硬體裝置的位置以及別的一些資訊,在使用者需要進行存取的時候就參照到裝置節點所記錄的資訊進行裝置的存取
如何從裝置節點中獲取資料
作業系統之所以把IO都抽象成了檔案,最大的好處就是可以通過統一的介面來存取這個檔案,從而和不同的裝置溝通。這些統一的介面就是作業系統針對檔案操作對外提供的一組系統呼叫:open函數、read函數、write函數等。比如,如果需要從一個裝置中獲取資料,只需要呼叫read函數去讀取該裝置對應的裝置節點就可以了,當然在read之前,要先呼叫open函數開啟。現在以獲取鍵盤輸入為例來介紹。
1、開啟裝置節點
在讀取裝置節點的資料之前,要先呼叫open函數開啟裝置節點。open函數的具體用法可以參考連結。簡單描述如下:
函數宣告:
int open(const char *pathname, int flags);
需要包含的標頭檔案:
#include <fcntl.h>
引數:
* 第一個引數(const char *pathname):表示需要開啟的檔案路徑
* 第二個引數(int flags):表示開啟檔案的方式,比如,」O_RDONLY」 ——唯讀開啟;」O_WRONLY」——只寫開啟;」O_RDWR」——讀、寫開啟,等。
返回值:
如果開啟成功,則返回該檔案的檔案描述符,以供read,write等函數使用。否則,返回-1。
那麼,要開啟鍵盤的裝置檔案(假設是」/dev/input/even10「),則需要以下程式碼:
int keys_fd; keys_fd = open("/dev/input/even10", O_RDONLY); if(keys_fd <= 0) { printf("open /dev/input/event10 device error!\n"); return -1; }
2 、讀取裝置節點的資料
讀取裝置節點需要使用read函數,具體使用方法可以參考連結。簡單介紹如下:
函數宣告:
ssize_t read(int fd, void *buf, size_t count);
需要包含的標頭檔案:
#include <unistd.h>
引數:
* 第一個引數(int fd):要開啟檔案的檔案描述符,來源一般是上述open函數的返回值。
* 第二個引數(void *buf):讀取到的資料存放的起始位置指標
* 第三個引數(size_t count):要讀取的資料位元組數
返回值:
* 如果讀取成功,則返回實際讀取到的位元組數
* 如果讀取失敗,則返回-1
* 如果返回值小於第三個引數count,則表示已經讀取到檔案結尾,返回值表示實際讀取的位元組數。
在讀取鍵盤的例子中,我們迴圈讀取鍵盤裝置的檔案節點,並將裝置儲存到一個char buf[24]的陣列中去。具體程式碼如下:
char buf[24]; while(1) { if(read(keys_fd, buf, 24) == 24) { // 成功的從裝置節點中獲取到了24個位元組 ... } }
根據read函數用法,當要讀取24個位元組,且read函數的返回值是24時,表示成功的從裝置節點中獲取到了24個位元組。
3、分析從裝置節點獲取的資料
為什麼這裡要從鍵盤的裝置驅動獲取24個位元組呢?這是因為正常情況下,從鍵盤裝置節點獲取的資料實際上是一個struct input_event結構。其定義為:
struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };
顯然,上述結構體的大小為24。
這裡需要理解的是:裝置節點是裝置驅動程式提供的,且裝置節點的資料是裝置驅動寫入的,而且寫入時,是以上述結構的規則寫入的,這是雙方通過<linux/input.h>約定好的,那麼應用程式去裝置節點中讀取資料之後,也需要按照上述結構去解析資料。那這個結構具體是什麼意思呢?
* struct timeval time:其大小為16個位元組,具體意義暫時不考慮。
* __u16 type:其大小為2個位元組,表示input裝置的型別,比如:EV_KEY表示上報的是鍵盤型別的資料,EV_REL表示相對路徑,滑鼠就屬於這種型別,還是其他等等。
* __u16 code:其大小為2個位元組,表示事件的程式碼。比如,如果type為EV_KEY,那麼該程式碼code為裝置鍵盤程式碼。code值實際上是應用程式和驅動程式約定好的一些固定的值,它可取的值位於include/uapi/linux/input-event-codes.h中。舉例來講,根據Linux原始碼下的include/uapi/linux/input-event-codes.h檔案的第91行#define KEY_Q 16,如果鍵盤上按下或鬆開了Q鍵,那麼鍵盤的驅動程式上報的code值應該是16;反之,如果應用程式獲取到的值是19,那麼,表示使用者按下或鬆開了鍵盤上的Q鍵。
* __s32 value:其大小為4個位元組,事件的值。如果事件的型別程式碼是EV_KEY,當按鍵按下時值為1,鬆開時值為0;
根據上述解釋,我們可以新增以下程式碼來解析從裝置節點中獲取的資料。
if(t.type == EV_KEY) // 我們只關心input event型別為EV_KEY(按鍵)的訊息 if(t.value == 0 || t.value == 1) { printf("key %d %s\n", t.code, // t.code表示按下或鬆開了哪個按鍵 (t.value) ? "Pressed" : "Released"); // t.value表示按下還是鬆開了相應的按鍵 }
4、關閉裝置節點
在從裝置節點獲取資料完成後,務必呼叫close函數,來關閉裝置節點。即
close(keys_fd);
相關推薦:《Linux視訊教學》
以上就是什麼是linux裝置節點的詳細內容,更多請關注TW511.COM其它相關文章!