1. 程式人生 > >Linux之poll機制分析

Linux之poll機制分析

for 可用 報告 超時時間 程序 訪問 events blank linux

應用程序訪問1個設備文件時可用阻塞/非阻塞方式.如果是使用阻塞方式,則直接調用open()、read()、write(),但是在驅動程序層會判斷是否可讀/可寫,如果不可讀/不可寫,則將當前進程休眠,直到被喚醒。如果是使用非阻塞方式,就需要采用poll/select機制,而且打開文件時標記文件的訪問權限位為O_NONBLOCK。

1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 

FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位

FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否為真

FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位

FD_ZERO(fd_set *set);用來清除描述詞組set的全部位

如果參數timeout設為:NULL:則表示select()沒有timeout,select將一直被阻塞,直到某個文件描述符上發生了事件。0:僅檢測描述符集合的狀態,然後立即返回,並不等待外部事件的發生。特定的時間值:如果在指定的時間段裏沒有事件發生,select將超時返回。

1 int poll(struct pollfd *fds, nfds_t nfds, int
timeout);這兩個函數其實本質類似.

fds 可以傳遞多個結構體,也就是說可以監測多個驅動設備所產生的事件,只要有一個產生了請求事件,就能立即返回

  struct pollfd {

    int fd; /* 文件描述符 */

    short events; /* 請求的事件類型,監視驅動文件的事件掩碼 */

    short revents; /* 驅動文件實際返回的事件 */

  } ;

nfds 監測驅動文件的個數

timeout 超時時間,單位為ms

事件類型events 可以為下列值:

POLLIN 有數據可讀

POLLRDNORM 有普通數據可讀,等效與POLLIN

POLLPRI 有緊迫數據可讀

POLLOUT 寫數據不會導致阻塞

POLLER 指定的文件描述符發生錯誤

POLLHUP 指定的文件描述符掛起事件

POLLNVAL 無效的請求,打不開指定的文件描述符

返回值

有事件發生 返回revents域不為0的文件描述符個數(也就是說事件發生,或者錯誤報告)

超時 返回0;

失敗   返回-1,並設置errno為錯誤類型

理解select模型:

理解select模型的關鍵在於理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每個bit 可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。

(1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。

(2)若fd=5,執行FD_SET(fd,&set);後set變為0001,0000(第5位置為1)

(3)若再加入fd=2,fd=1,則set變為0001,0011

(4)執行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。

註意:沒有事件 發生的fd=5被清空。

從內核態理解poll機制

我們從應用程序直接調用poll函數,系統會走以下流程

 1 app:poll
 2 kernel:sys_poll
 3             do_sys_poll
 4                 poll_initwait(&table)
 5                 do_poll(nfds, head, &table, timeout)
 6                         for (;;) {
 7                             for (; pfd != pfd_end; pfd++) {    /* 可以監測多個驅動設備所產生的事件 */
 8                                 if (do_pollfd(pfd, pt)) {   
 9                                     count++;
10                                     pt = NULL;
11                                 }
12                                 if (count || !*timeout || signal_pending(current))
13                                     break;
14                                 __timeout = schedule_timeout(__timeout);
15                             }
16                         }
17 
18 
19 do_pollfd(pfd, pt){
20 ...
21 if (file->f_op && file->f_op->poll)
22     mask = file->f_op->poll(file, pwait);
23     return mask;                
24 ...
25 }基於內核源碼版本為linux-2.6.22.6

使用poll_initwait(&table),就是將__pollwait設為回調函數,後面會去調用驅動程序的poll函數,poll函數調用pollwait就等於調用__pollwait,將當前進程加入到等待隊列中。然後一直在循環,do_pollfd就是去調用驅動程序的poll函數,驅動程序的poll函數,poll函數開始調用pollwait就等於調用__pollwait回調函數,將當前進程加入到等待隊列中,以便喚醒休眠後的當前進程。然後返回當前驅動設備的狀態(mask).
如果do_pollfd返回的mask為非0,即count非0,就會馬上返回,應用程序就可以使用FD_ISSET了解此時設備狀態。當然,如果超時或者此進程有其他信號要處理超時,此進程有其他信號要處理,也會馬上返回,但是應用程序使用FD_ISSET了解到此時設備狀態還是不可用時,又繼續輪詢。如果do_pollfd返回的mask為0,而且未超時且未有其他信號發生,就會進程調度,讓此進程休眠。在前面已經將此進程加入到驅動程序的等待隊列中了,如果設備可用時,就會喚醒等待隊列中的進程,也就喚醒了此進程,又去poll_initwait(&table).

參考:https://www.cnblogs.com/amanlikethis/p/6915485.html

http://www.cnblogs.com/shihaochangeworld/p/5747490.html

Linux之poll機制分析