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、開啟裝置節點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。
这里需要理解的是:设备节点是设备驱动程序提供的,且设备节点的数据是设备驱动写入的,而且写入时,是以上述结构的规则写入的,这是双方通过b8aef83627d8109d104c626ed7bbfbbd约定好的,那么应用程序去设备节点中读取数据之后,也需要按照上述结构去解析数据。那这个结构具体是什么意思呢?
* 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設備節點的詳細內容。更多資訊請關注PHP中文網其他相關文章!