Linux裝置驅動中的阻塞和非阻塞I/O
今天寫的是Linux裝置驅動中的阻塞和非阻塞I/0,何謂阻塞與非阻塞I/O?簡單來說就是對I/O操作的兩種不同的方式,驅動程式可以靈活的支援使用者空間對裝置的這兩種訪問方式。
一、基本概念:
- 阻塞操作 : 是指在執行裝置操作時,若不能獲得資源,則掛起程式直到滿足操作條件後再進行操作。被掛起的程式進入休眠, 被從排程器移走,直到條件滿足。
- 非阻塞操作 :在不能進行裝置操作時,並不掛起,它或者放棄,或者不停地查詢,直到可以進行操作。非阻塞應用程式通常使用select系統呼叫查詢是否可以對裝置進行無阻塞的訪問最終會引發裝置驅動中 poll函式執行。
二、輪詢操作
阻塞的讀取一個字元:
char buf; fd = open("/dev/ttyS1",O_RDWR); ..... res = read(fd,&buf,1); //當串列埠上有輸入時才返回,沒有輸入則程式掛起睡眠 if(res == 1) { printf("%c/n",buf); }
非阻塞的讀一個字元:
char buf; fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞標識 ..... while(read(fd,&buf,1)!=1);//串列埠上沒有輸入則返回,所以迴圈讀取 printf("%c/n",buf);
阻塞操作常常用等待佇列來實現,而非阻塞操作用輪詢的方式來實現。非阻塞I/O的操作在應用層通常會用到select()和poll()系統呼叫查詢是否可對裝置進行無阻塞訪問。select()和poll()系統呼叫最終會引發裝置驅動中的poll()函式被呼叫。這裡對佇列就不多介紹了,大家可以看看資料結構裡面的知識點。
應用層的select()原型為:
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptionfds, struct timeval *timeout); numfds 的值為需要檢查的號碼最高的檔案描述符加1,若select()在等待timeout時間後,若沒有檔案描述符準備好則返回。
應用程式為:
#inlcude------ main() { int fd,num; char rd_ch[BUFFER_LEN]; fd_set rfds,wfds; //讀寫檔案描述符集 //以非阻塞方式開啟/dev/globalfifo裝置檔案 fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK); if(fd != -1) { //FIFO 清零 if(ioctl(fd,FIFO_CLEAR,0) < 0) { printf("ioctl cmd failed /n"); } while(1) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd,&rfds); FD_SET(fd,&wfds); select(fd+1,&rfds,&wfds,null,null); } } }
下面說說裝置驅動中的poll()函式,函式原型如下:
static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一個引數是file結構體指標,第三個引數是輪詢表指標,這個函式應該進行兩項工作
- 對可能引起裝置檔案狀態變化的等待佇列呼叫poll_wait()函式,將對應的等待佇列頭新增到poll_table
- 返回表示是否能對裝置進行無阻塞讀,寫訪問的掩碼
這裡還要提到poll_wait()函式,很多人會以為是和wait_event()一樣的函式,會阻塞的等待某件事情的發生,其實這個函式並不會引起阻塞,它的工作是把當前的程式增添到wait引數指定的等待列表poll_table中去,poll_wait()函式原型如下:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) 從中可以看出是將等待佇列頭wait_address新增到p所指向的結構體中(poll_table)
驅動函式中的poll()函式典型模板如下:
static unsigned int xxx_poll(struct file *filp,struct socket *sock, poll_table *wait) { unsigned int mask = 0; struct xxx_dev *dev = filp->private_data;//獲得裝置結構體指標 ... poll_wait(filp,&dev->r_wait,wait);//加讀等待佇列頭到poll_table poll_wait(filp,&dev->w_wait,wait);//加寫等待佇列頭到poll_table ... if(...)//可讀 mask |= POLLIN | POLLRDNORM; if(...)//可寫 mask |= POLLOUT | POLLRDNORM; ... return mask; }
三、支援輪詢操作的globalfifo驅動
在globalfifo的poll()函式中,首先將裝置結構體重的r_wait和w_wait等待佇列頭加到等待佇列表,globalfifo裝置驅動的poll()函式如下:
static unsigned int gloablfif0_poll(struct file *filp,poll_table *wait) { unsigned int mask = 0; struct globalfifo_dev *dev = filp->private_data; down(&dev->sem); poll_wait(filp,&dev->r_wait , wait) ; poll_wait(filp,&dev->r_wait , wait) ; if(dev->current_len != 0) { mask |= POLLIN | POLLRDNORM; } if(dev->current_len != GLOBALFIFO_SIZE) { mask |= POLLOUT | POLLWRNORM; } up(&dev->sem); return mask; }
四、總結
阻塞與非阻塞操作:
- 定義並初始化等待對列頭;
- 定義並初始化等待佇列;
- 把等待佇列新增到等待佇列頭
- 設定程式狀態(TASK_INTERRUPTIBLE(可以被訊號打斷)和TASK_UNINTERRUPTIBLE(不能被訊號打斷))
- 呼叫其它程式
poll機制:
- 把等待佇列頭加到poll_table
- 返回表示是否能對裝置進行無阻塞讀,寫訪問的掩碼
相關文章
- Java中I/O流:阻塞和非阻塞範例Java
- 【linux】驅動-13-阻塞與非阻塞Linux
- 驅動Driver-阻塞&非阻塞
- socket阻塞與非阻塞,同步與非同步、I/O模型非同步模型
- 使用Task實現非阻塞式的I/O操作
- Java非阻塞I/O模型之NIO說明Java模型
- 談談對不同I/O模型的理解 (阻塞/非阻塞IO,同步/非同步IO)模型非同步
- 從linux原始碼看socket的阻塞和非阻塞Linux原始碼
- 從 Linux 原始碼看 socket 的阻塞和非阻塞Linux原始碼
- Linux 阻塞和非阻塞 IO 實驗學習Linux
- 流?I/O 操作?阻塞?epoll?
- FastAPI之阻塞式io和非阻塞式ioASTAPI
- 阻塞IO與非阻塞IO
- 同步非同步,阻塞非阻塞非同步
- 非同步、同步、阻塞、非阻塞非同步
- ♻️同步和非同步;並行和併發;阻塞和非阻塞非同步並行
- 同步、非同步,阻塞、非阻塞理解非同步
- 同步、非同步、阻塞與非阻塞非同步
- 同步非同步 與 阻塞非阻塞非同步
- 理解阻塞、非阻塞、同步、非同步非同步
- 同步、非同步、阻塞、非阻塞的區別非同步
- IO - 同步 非同步 阻塞 非阻塞的區別非同步
- 如何解讀 Java IO、NIO 中的同步阻塞與同步非阻塞?Java
- 對執行緒、協程和同步非同步、阻塞非阻塞的理解執行緒非同步
- 同步阻塞、同步非阻塞、多路複用的介紹
- Linux驅動之I2C匯流排裝置以及驅動Linux
- 阻塞式程式設計和非阻塞式程式設計區別程式設計
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- [作業系統]阻塞io 非阻塞io Epoll作業系統
- 徹底搞懂同步非同步與阻塞非阻塞非同步
- 基於 mysql 非同步驅動的非阻塞 Mybatis 瞭解一下MySql非同步MyBatis
- Java 非阻塞 IO 和非同步 IOJava非同步
- 阻塞非阻塞和同步非同步的區分 參考一些書籍非同步
- java同步非阻塞IOJava
- 怎樣理解阻塞非阻塞與同步非同步的區別?非同步
- 【死磕NIO】— 阻塞IO,非阻塞IO,IO複用,訊號驅動IO,非同步IO,這你真的分的清楚嗎?非同步
- 如何給女朋友解釋什麼是IO中的阻塞、非阻塞、同步、非同步?非同步
- 對於同步、非同步、阻塞、非阻塞的幾點淺薄理解非同步
- 大白話搞懂什麼是同步/非同步/阻塞/非阻塞非同步