來源:百問網
作者:韋東山
本文字數:2344,閱讀時長:4分鐘
1.適用場景
在前面引入中斷時,我們曾經舉過一個例子:
媽媽怎么知道臥室里小孩醒了?
- 時不時進房間看一下:查詢方式
簡單,但是累 - 進去房間陪小孩一起睡覺,小孩醒了會吵醒她:休眠-喚醒
不累,但是媽媽干不了活了 - 媽媽要干很多活,但是可以陪小孩睡一會,定個鬧鐘:poll方式
要浪費點時間,但是可以繼續干活。媽媽要么是被小孩吵醒,要么是被鬧鐘吵醒。 - 媽媽在客廳干活,小孩醒了他會自己走出房門告訴媽媽:異步通知
媽媽、小孩互不耽誤
使用休眠-喚醒的方式等待某個事件發生時,有一個缺點:等待的時間可能很久。我們可以加上一個超時時間,這時就可以使用poll機制。
- App不知道驅動程序中是否有數據,可以先調用poll函數查詢一下,poll函數可以傳入超時時間;
- APP進入內核態,調用到驅動程序的poll函數,如果有數據的話立刻返回;
- 如果發現沒有數據時就休眠一段時間;
- 當有數據時,比如當按下按鍵時,驅動程序的中斷服務程序被調用,它會記錄數據、喚醒APP;
- 當超時時間到了之后,內核也會喚醒APP;
- APP根據poll函數的返回值就可以知道是否有數據,如果有數據就調用read得到數據
2.使用流程
媽媽進入房間時,會先看小孩醒沒醒,鬧鐘響之后走出房間之前又會再看小孩醒沒醒。
注意:看了2次小孩!
POLL機制也是類似的,流程如下:
函數執行流程如上圖①~⑧所示,重點從③開始看。假設一開始無按鍵數據:
③ APP調用poll之后,進入內核態;
④ 導致驅動程序的drv_poll被調用:
注意,drv_poll要把自己這個線程掛入等待隊列wq中;假設不放入隊列里,那以后發生中斷時,中斷服務程序去哪里找到你嘛?
drv_poll還會判斷一下:有沒有數據啊?返回這個狀態。
⑤ 假設當前沒有數據,則休眠一會;
⑥ 在休眠過程中,按下了按鍵,發生了中斷:
在中斷服務程序里記錄了按鍵值,并且從wq中把線程喚醒了。
⑦ 線程從休眠中被喚醒,繼續執行for循環,再次調用drv_poll:
drv_poll返回數據狀態
⑧ 哦,你有數據,那從內核態返回到應用態吧
⑨ APP調用read函數讀數據
如果一直沒有數據,調用流程也是類似的,重點從③開始看,如下:
③ APP調用poll之后,進入內核態;
④ 導致驅動程序的drv_poll被調用:
注意,drv_poll要把自己這個線程掛入等待隊列wq中;假設不放入隊列里,那以后發生中斷時,中斷服務程序去哪里找到你嘛?
drv_poll還會判斷一下:有沒有數據啊?返回這個狀態。
⑤ 假設當前沒有數據,則休眠一會;
⑥ 在休眠過程中,一直沒有按下了按鍵,超時時間到:內核把這個線程喚醒;
⑦ 線程從休眠中被喚醒,繼續執行for循環,再次調用drv_poll:
drv_poll返回數據狀態
⑧ 哦,你還是沒有數據,但是超時時間到了,那從內核態返回到應用態吧
⑨ APP不能調用read函數讀數據
注意幾點:
- drv_poll要把線程掛入隊列wq,但是并不是在drv_poll中進入休眠,而是在調用drv_poll之后休眠
- drv_poll要返回數據狀態
- APP調用一次poll,有可能會導致drv_poll被調用2次
- 線程被喚醒的原因有2:中斷發生了去隊列wq中把它喚醒,超時時間到了內核把它喚醒
- APP要判斷poll返回的原因:有數據,還是超時。有數據時再去調用read函數。
3. 驅動編程
使用poll機制時,驅動程序的核心就是提供對應的drv_poll函數。
在drv_poll函數中要做2件事:
- 把當前線程掛入隊列wq:poll_wait
APP調用一次poll,可能導致drv_poll被調用2次,但是我們并不需要把當前線程掛入隊列2次。可以使用內核的函數poll_wait把線程掛入隊列,如果線程已經在隊列里了,它就不會再次掛入。 - 返回設備狀態:
APP調用poll函數時,有可能是查詢“有沒有數據可以讀”:POLLIN,也有可能是查詢“你有沒有空間給我寫數據”:POLLOUT。所以drv_poll要返回自己的當前狀態:(POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。POLLRDNORM等同于POLLIN,為了兼容某些APP把它們一起返回。POLLWRNORM等同于POLLOUT ,為了兼容某些APP把它們一起返回。
APP調用poll后,很有可能會休眠。對應的,在按鍵驅動的中斷服務程序中,也要有喚醒操作。
驅動程序中poll的代碼如下:
static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{ printk("%s %s line %dn", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp, &gpio_key_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}
4 應用編程
注意:APP可以調用poll或select函數,這2個函數的作用是一樣的。
poll/select函數可以監測多個文件,可以監測多種事件:
事件類型 說明
POLLIN 有數據可讀
POLLRDNORM 等同于POLLIN
POLLRDBAND Priority band data can be read,有優先級較較高的“band data”可讀
linux系統中很少使用這個事件
POLLPRI 高優先級數據可讀
POLLOUT 可以寫數據
POLLWRNORM 等同于POLLOUT
POLLWRBAND Priority data may be written
POLLERR 發生了錯誤
POLLHUP 掛起
POLLNVAL 無效的請求,一般是fd未open