Linux學習系列-輪詢函數

理解這三個輪詢函數差異的關鍵在於理解其輪詢的文件描述符(socket也是文件)的數據結構。

select輪詢函數

函數定義:

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

// fd_set操作宏
void FD_SET(int fd, fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
void FD_ISSET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
  • nfds參數指定被監聽的文件描述符總數;
  • readfds、writefds、exeptfds參數分別指向可讀、可寫和異常事件的accept到的對應的文件描述符集合;fd_set中用一個整型數組的每一個元素的每一位(bit)來表示每一個文件描述符是否有相應的事件發生(0表示有;1表示沒有),那麼理論上fd_set就可以表示【數組長度*32】個文件描述符,而數組長度由FD_SETSIZE 來設置,一般是1024。在select之前,每次先調用FD_SET()設置accept到的文件描述符標記位爲1,表示需要監聽這個文件描述符需要監聽可讀、可寫或者異常事件;當有可讀、可寫和異常事件發生時,在select調用過程中,內核會修改相應的這三個文件描述符集合中的沒有事件發生的文件描述符標記位爲0;在select之後,應用程序就可以輪詢accept到的所有文件描述符,並通過FD_ISSET來判斷該文件描述符是否有事件發生(fd_set中對應的文件描述符標記位是1表示有事件發生)。
  • timeout就是輪詢時間片,表示多久輪詢一次。

這種函數設計實現,有三個缺陷:

  1. 監聽的事件類型有限,只有3種;
  2. readfds、writefds和exeptfds有兩重角色,第一層是當做要監聽的文件描述符集合來傳給內核監聽;第二層是當做發生事件的文件描述符結果集合。因此每次輪詢處理完事件,需要重新設置需要監聽的文件描述符;
  3. 事件和文件描述符沒有綁定,因此要處理事件需要輪詢所有的已accept到的文件描述符。

poll輪詢函數

函數定義:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd
{
    int fd;  //文件描述符
    short events;  //註冊的事件
    short revents  //實際發生的事件,由內核填充
}
  • fds是需要監聽的文件描述符集合,它是一個pollfd結構體的數組。
  • nfds是監聽的文件描述符的數量,也就是fds數組的長度。

說明:與select相比這種函數設計清晰簡潔,將文件描述符和事件通過pollfd結構體來進行了綁定,並且區分了註冊的時間和實際發生的事件。但是其和select一樣,最終輪詢的結果都是所有已經註冊的文件描述符的集合。

epoll輪詢函數

函數定義:

/* 創建內核事件表,返回的就是事件表的文件描述符 */
int epoll_create(int size); 
/* 往內核事件表註冊、修改、刪除指定fd上的事件 */
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 
/* 輪詢檢測事件 */
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • epoll和select、poll有明顯不同,其提供了一套函數。它會在內核開闢一個事件表用於存儲註冊的事件。
  • epoll_ctl中的epfd就是epoll_create的返回值,表示內核事件表的文件描述符;op就是操作,包括註冊、刪除、修改;fd表示要操作的文件描述符;event表示要註冊到fd上的事件,需要特別之處epoll_event結構實現了事件和文件描述符的綁定。
  • epoll_wait是輪詢函數,如果檢測到事件,就將所有就緒的事件從內核事件表中的拷貝到第二個參數指向的數組events中。這和select、poll有明顯區別,這提高了應用程序輪詢就緒事件的效率。
  • epoll還有一個特殊的地方在於,其存在兩種模式:LT(電平觸發)和ET(邊沿觸發)。LT是默認工作模式,而當往內核事件表註冊一個文件描述符的EPOLLET事件時,epoll將以ET模式來操作文件描述符。ET是一種高效模式。LT模式下,當epoll_wait檢測到事件時,應用程序可以不用立即處理,下次epoll_wait時還會再次通告應用程序;ET模式下,應用程序必須立即處理事件,因此避免了epoll_wait重複觸發事件的次數,因此較高效。

統一事件源

我們這裏講的統一事件源中的事件源是指IO事件和信號。信號原理可以參見《Linux學習系列-信號》。通常我們通過輪詢函數來處理IO事件,既然要統一,那麼自然也要使用輪詢函數來處理信號。典型的處理方案是:信號發生時,信號處理函數一般通過管道來通知程序主循環信號值,那麼主循環中的輪詢函數就可以通過輪詢函數來監聽管道上的IO事件即可。這樣就實現了IO事件和信號的統一處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章