嵌入式核心及驅動開發之學習筆記(九) 多路複用+中斷實現讀取資料
使用阻塞模型雖然可以避免等待資料過程中,CPU對程序的消耗,但是僅僅是為了等待這一個結果,就讓程序進入休眠,對於還要進行其他IO操作的程序而言太“奢侈”。所以引入多路複用的概念,解決這個問題。
非阻塞:立即返回結果,如果想得到期望的結果,要不停的呼叫這個方法(輪詢),非常耗費資源
阻塞:沒有得到真正的資料前,不返回結果。此時,程序進入阻塞(休眠)態,直到有資料喚醒程序,這個過程不耗資源。
多路複用:和原理和阻塞類似,不過多路複用是同時進行多個IO讀寫操作。這樣就減少了在等待時間上的浪費,提高效率。
多路複用要對檔案描述符進行操作,在應用層上進行實現。關於多路複用在應用層上的使用,之前我有總結過一份
- poll函式實現對標準輸入流(按鍵)與裝置輸入流(按鍵)的監控
- 在裝置驅動中實現poll的介面
Linux環境下使用`man 2 poll`檢視poll函式的介紹,程式包含標頭檔案poll.h
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
引數1: 表示多個檔案描述符集合
struct pollfd描述的是檔案描述符到資訊
struct pollfd {
int fd; //檔案描述符
short events; //希望監控fd的什麼事件:讀,寫,出錯
POLLIN 讀,
POLLOUT 寫,
POLLERR出錯
short revents; //結果描述,表示當前的fd是否有讀,寫,出錯
//用於判斷,是核心自動賦值
POLLIN 讀,
POLLOUT 寫,
POLLERR出錯
};
引數2:被監控到fd的個數
引數3: 監控的時間:
正: 表示監控多少ms
負數: 無限的時間去監控
0: 等待0ms,類似於非阻賽
返回值: 負數:出錯
大於0,表示fd中有資料
等於0: 時間到
應用程式
//key_test.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <poll.h> struct key_event{ int code; // 按鍵的型別 int value; // 狀態 }; #define KEY_ENTER 28 int main(int argc, char *argv[]) { int ret; char in_buf[128]; //接收標準輸入資料 struct key_event event; //接收按鍵裝置資料 int fd = open("/dev/key0", O_RDWR); if(fd < 0) { perror("open"); exit(1); } //描述2個檔案描述符,標準輸入0 和 按鍵裝置結點fd struct pollfd pfd[2]; pfd[0].fd = fd; //監控按鍵裝置 pfd[0].events = POLLIN; pfd[1].fd = 0; //標準輸入 pfd[1].events = POLLIN; while(1) { ret = poll(pfd, 2, -1); // 多路複用 poll 同時監控按鍵與標準輸入 printf("ret = %d\n", ret); if(ret > 0) {//表示有資料 if(pfd[0].revents & POLLIN) {//按鍵裝置 //讀裝置結點中的資料 read(pfd[0].fd, &event, sizeof(struct key_event)); if(event.code == KEY_ENTER) { if(event.value) { printf("APP__ key enter pressed\n"); }else { printf("APP__ key enter up\n"); } } } if(pfd[1].revents & POLLIN) {//標準輸入 fgets(in_buf, 128, stdin); printf("in_buf = %s\n", in_buf); } }else{ perror("poll"); exit(1); } } close(pfd[0].fd); return 0; }
裝置驅動檔案
1.在file_operations中 poll指向key_drv_poll函式
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
.poll = key_drv_poll,
};
2.在key_drv_poll函式中實現介面
unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts)
{
// 返回一個mask值
unsigned int mask;
// 呼叫poll_wait,將當前到等待佇列註冊系統中
poll_wait(filp, &key_dev->wq_head, pts);
// 1,當沒有資料到時候返回一個0
if(!key_dev->key_state)
mask = 0;
// 2,有資料返回一個POLLIN
if(key_dev->key_state)
mask |= POLLIN;
return mask;
}