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函數打開?,F在以獲取鍵盤輸入為例來介紹。
1、打開設備節點
在讀取設備節點的數據之前,要先調用open函數打開設備節點。open函數的具體用法可以參考鏈接。簡單描述如下:
函數聲明:
需要包含的頭文件:
#include?<fcntl.h></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?<p>2 、讀取設備節點的數據</p><p>讀取設備節點需要使用read函數,具體使用方法可以參考鏈接。簡單介紹如下:</p><p>函數聲明:</p><pre class="brush:js;toolbar:false"> ssize_t?read(int?fd,?void?*buf,?size_t?count);
需要包含的頭文件:
#include?<unistd.h></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。
這里需要理解的是:設備節點是設備驅動程序提供的,且設備節點的數據是設備驅動寫入的,而且寫入時,是以上述結構的規則寫入的,這是雙方通過
* 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?%sn",? ????????t.code,?????????????????????//?t.code表示按下或松開了哪個按鍵 ????????(t.value)???"Pressed"?:?"Released");???//?t.value表示按下還是松開了相應的按鍵 ????}
4、關閉設備節點
在從設備節點獲取數據完成后,務必調用close函數,來關閉設備節點。即
close(keys_fd);
相關推薦:《Linux視頻教程》