linux 内核驱动的poll,Linux驱动基石之POLL机制
來源:百問網(wǎng)
作者:韋東山
本文字?jǐn)?shù):2344,閱讀時(shí)長(zhǎng):4分鐘
1.適用場(chǎng)景
在前面引入中斷時(shí),我們?cè)?jīng)舉過一個(gè)例子:
媽媽怎么知道臥室里小孩醒了?
時(shí)不時(shí)進(jìn)房間看一下:查詢方式
簡(jiǎn)單,但是累
進(jìn)去房間陪小孩一起睡覺,小孩醒了會(huì)吵醒她:休眠-喚醒
不累,但是媽媽干不了活了
媽媽要干很多活,但是可以陪小孩睡一會(huì),定個(gè)鬧鐘:poll方式
要浪費(fèi)點(diǎn)時(shí)間,但是可以繼續(xù)干活。媽媽要么是被小孩吵醒,要么是被鬧鐘吵醒。
媽媽在客廳干活,小孩醒了他會(huì)自己走出房門告訴媽媽:異步通知
媽媽、小孩互不耽誤
使用休眠-喚醒的方式等待某個(gè)事件發(fā)生時(shí),有一個(gè)缺點(diǎn):等待的時(shí)間可能很久。我們可以加上一個(gè)超時(shí)時(shí)間,這時(shí)就可以使用poll機(jī)制。
App不知道驅(qū)動(dòng)程序中是否有數(shù)據(jù),可以先調(diào)用poll函數(shù)查詢一下,poll函數(shù)可以傳入超時(shí)時(shí)間;
APP進(jìn)入內(nèi)核態(tài),調(diào)用到驅(qū)動(dòng)程序的poll函數(shù),如果有數(shù)據(jù)的話立刻返回;
如果發(fā)現(xiàn)沒有數(shù)據(jù)時(shí)就休眠一段時(shí)間;
當(dāng)有數(shù)據(jù)時(shí),比如當(dāng)按下按鍵時(shí),驅(qū)動(dòng)程序的中斷服務(wù)程序被調(diào)用,它會(huì)記錄數(shù)據(jù)、喚醒APP;
當(dāng)超時(shí)時(shí)間到了之后,內(nèi)核也會(huì)喚醒APP;
APP根據(jù)poll函數(shù)的返回值就可以知道是否有數(shù)據(jù),如果有數(shù)據(jù)就調(diào)用read得到數(shù)據(jù)
2.使用流程
媽媽進(jìn)入房間時(shí),會(huì)先看小孩醒沒醒,鬧鐘響之后走出房間之前又會(huì)再看小孩醒沒醒。
注意:看了2次小孩!
POLL機(jī)制也是類似的,流程如下:
函數(shù)執(zhí)行流程如上圖①~⑧所示,重點(diǎn)從③開始看。假設(shè)一開始無按鍵數(shù)據(jù):
③ APP調(diào)用poll之后,進(jìn)入內(nèi)核態(tài);
④ 導(dǎo)致驅(qū)動(dòng)程序的drv_poll被調(diào)用:
注意,drv_poll要把自己這個(gè)線程掛入等待隊(duì)列wq中;假設(shè)不放入隊(duì)列里,那以后發(fā)生中斷時(shí),中斷服務(wù)程序去哪里找到你嘛?
drv_poll還會(huì)判斷一下:有沒有數(shù)據(jù)啊?返回這個(gè)狀態(tài)。
⑤ 假設(shè)當(dāng)前沒有數(shù)據(jù),則休眠一會(huì);
⑥ 在休眠過程中,按下了按鍵,發(fā)生了中斷:
在中斷服務(wù)程序里記錄了按鍵值,并且從wq中把線程喚醒了。
⑦ 線程從休眠中被喚醒,繼續(xù)執(zhí)行for循環(huán),再次調(diào)用drv_poll:
drv_poll返回?cái)?shù)據(jù)狀態(tài)
⑧ 哦,你有數(shù)據(jù),那從內(nèi)核態(tài)返回到應(yīng)用態(tài)吧
⑨ APP調(diào)用read函數(shù)讀數(shù)據(jù)
如果一直沒有數(shù)據(jù),調(diào)用流程也是類似的,重點(diǎn)從③開始看,如下:
③ APP調(diào)用poll之后,進(jìn)入內(nèi)核態(tài);
④ 導(dǎo)致驅(qū)動(dòng)程序的drv_poll被調(diào)用:
注意,drv_poll要把自己這個(gè)線程掛入等待隊(duì)列wq中;假設(shè)不放入隊(duì)列里,那以后發(fā)生中斷時(shí),中斷服務(wù)程序去哪里找到你嘛?
drv_poll還會(huì)判斷一下:有沒有數(shù)據(jù)啊?返回這個(gè)狀態(tài)。
⑤ 假設(shè)當(dāng)前沒有數(shù)據(jù),則休眠一會(huì);
⑥ 在休眠過程中,一直沒有按下了按鍵,超時(shí)時(shí)間到:內(nèi)核把這個(gè)線程喚醒;
⑦ 線程從休眠中被喚醒,繼續(xù)執(zhí)行for循環(huán),再次調(diào)用drv_poll:
drv_poll返回?cái)?shù)據(jù)狀態(tài)
⑧ 哦,你還是沒有數(shù)據(jù),但是超時(shí)時(shí)間到了,那從內(nèi)核態(tài)返回到應(yīng)用態(tài)吧
⑨ APP不能調(diào)用read函數(shù)讀數(shù)據(jù)
注意幾點(diǎn):
drv_poll要把線程掛入隊(duì)列wq,但是并不是在drv_poll中進(jìn)入休眠,而是在調(diào)用drv_poll之后休眠
drv_poll要返回?cái)?shù)據(jù)狀態(tài)
APP調(diào)用一次poll,有可能會(huì)導(dǎo)致drv_poll被調(diào)用2次
線程被喚醒的原因有2:中斷發(fā)生了去隊(duì)列wq中把它喚醒,超時(shí)時(shí)間到了內(nèi)核把它喚醒
APP要判斷poll返回的原因:有數(shù)據(jù),還是超時(shí)。有數(shù)據(jù)時(shí)再去調(diào)用read函數(shù)。
3. 驅(qū)動(dòng)編程
使用poll機(jī)制時(shí),驅(qū)動(dòng)程序的核心就是提供對(duì)應(yīng)的drv_poll函數(shù)。
在drv_poll函數(shù)中要做2件事:
把當(dāng)前線程掛入隊(duì)列wq:poll_wait
APP調(diào)用一次poll,可能導(dǎo)致drv_poll被調(diào)用2次,但是我們并不需要把當(dāng)前線程掛入隊(duì)列2次。可以使用內(nèi)核的函數(shù)poll_wait把線程掛入隊(duì)列,如果線程已經(jīng)在隊(duì)列里了,它就不會(huì)再次掛入。
返回設(shè)備狀態(tài):
APP調(diào)用poll函數(shù)時(shí),有可能是查詢“有沒有數(shù)據(jù)可以讀”:POLLIN,也有可能是查詢“你有沒有空間給我寫數(shù)據(jù)”:POLLOUT。所以drv_poll要返回自己的當(dāng)前狀態(tài):(POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。POLLRDNORM等同于POLLIN,為了兼容某些APP把它們一起返回。POLLWRNORM等同于POLLOUT ,為了兼容某些APP把它們一起返回。
APP調(diào)用poll后,很有可能會(huì)休眠。對(duì)應(yīng)的,在按鍵驅(qū)動(dòng)的中斷服務(wù)程序中,也要有喚醒操作。
驅(qū)動(dòng)程序中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 應(yīng)用編程
注意:APP可以調(diào)用poll或select函數(shù),這2個(gè)函數(shù)的作用是一樣的。
poll/select函數(shù)可以監(jiān)測(cè)多個(gè)文件,可以監(jiān)測(cè)多種事件:
事件類型 說明
POLLIN 有數(shù)據(jù)可讀
POLLRDNORM 等同于POLLIN
POLLRDBAND Priority band data can be read,有優(yōu)先級(jí)較較高的“band data”可讀
linux系統(tǒng)中很少使用這個(gè)事件
POLLPRI 高優(yōu)先級(jí)數(shù)據(jù)可讀
POLLOUT 可以寫數(shù)據(jù)
POLLWRNORM 等同于POLLOUT
POLLWRBAND Priority data may be written
POLLERR 發(fā)生了錯(cuò)誤
POLLHUP 掛起
POLLNVAL 無效的請(qǐng)求,一般是fd未open
總結(jié)
以上是生活随笔為你收集整理的linux 内核驱动的poll,Linux驱动基石之POLL机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ios怎么换区
- 下一篇: linux非权限安装bioperl,Bi