I/O多路複用——select函數與poll函數

1 區別

  同:(1)機制類似,本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理。(2)包含大量文件描述符的數組被整體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增加而線性增大。

  異:poll沒有最大文件描述符數量的限制。

2 select函數原型

  該函數准許進程指示內核等待多個事件中的任何一個發送,並只在有一個或多個事件發生或經歷一段指定的時間後才喚醒。函數原型如下: 

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
                               返回值:就緒描述符的數目,超時返回0,出錯返回-1

  

函數參數介紹如下:

(1)第一個參數maxfdp1指定待測試的描述字個數,它的值是待測試的最大描述字加1(因此把該參數命名爲maxfdp1),描述字0、1、2...maxfdp1-1均將被測試。

因爲文件描述符是從0開始的。

(2)中間的三個參數readset、writeset和exceptset指定我們要讓內核測試讀、寫和異常條件的描述字。如果對某一個的條件不感興趣,就可以把它設爲空指針。struct fd_set可以理解爲一個集合,這個集合中存放的是文件描述符,可通過以下四個宏進行設置:

          void FD_ZERO(fd_set *fdset);           //清空集合

          void FD_SET(int fd, fd_set *fdset);   //將一個給定的文件描述符加入集合之中

          void FD_CLR(int fd, fd_set *fdset);   //將一個給定的文件描述符從集合中刪除

          int FD_ISSET(int fd, fd_set *fdset);   // 檢查集合中指定的文件描述符是否可以讀寫 

(3)timeout告知內核等待所指定描述字中的任何一個就緒可花多少時間。其timeval結構用於指定這段時間的秒數和微秒數。

         struct timeval{

                   long tv_sec;   //seconds

                   long tv_usec;  //microseconds

       };

這個參數有三種可能:

(1)永遠等待下去:僅在有一個描述字準備好I/O時才返回。爲此,把該參數設置爲空指針NULL。

(2)等待一段固定時間:在有一個描述字準備好I/O時返回,但是不超過由該參數所指向的timeval結構中指定的秒數和微秒數。

(3)根本不等待:檢查描述字後立即返回,這稱爲輪詢。爲此,該參數必須指向一個timeval結構,而且其中的定時器值必須爲0。

  select函數的使用場景。注:圖片來源於偉大的互聯網

  

3 poll函數原型

  函數原型如下:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
        成功時,返回結構體中revents域不爲0的文件描述符個數;如果在超時前沒有任何事件發生,poll()返回0;失敗時,poll()返回-1

  pollfd結構體定義如下:

struct pollfd {

int fd;         /* 文件描述符 */
short events;         /* 等待的事件 */
short revents;       /* 實際發生了的事件 */
} ; 

  每一個pollfd結構體指定了一個被監視的文件描述符,可以傳遞多個結構體,指示poll()監視多個文件描述符。每個結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域。revents域是文件描述符的操作結果事件掩碼,內核在調用返回時設置這個域。events域中請求的任何事件都可能在revents域中返回。合法的事件如下:

  POLLIN         有數據可讀。

  POLLRDNORM       有普通數據可讀。

  POLLRDBAND      有優先數據可讀。

  POLLPRI         有緊迫數據可讀。

  POLLOUT            寫數據不會導致阻塞。

  POLLWRNORM       寫普通數據不會導致阻塞。

  POLLWRBAND        寫優先數據不會導致阻塞。

  POLLMSGSIGPOLL     消息可用。

  此外,revents域中還可能返回下列事件:
  POLLER     指定的文件描述符發生錯誤。

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

  POLLNVAL  指定的文件描述符非法。

這些事件在events域中無意義,因爲它們在合適的時候總是會從revents中返回。

  使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。
  POLLIN | POLLPRI等價於select()的讀事件,POLLOUT |POLLWRBAND等價於select()的寫事件。POLLIN等價於POLLRDNORM |POLLRDBAND,而POLLOUT則等價於POLLWRNORM。例如,要同時監視一個文件描述符是否可讀和可寫,我們可以設置 events爲POLLIN |POLLOUT。在poll返回時,我們可以檢查revents中的標誌,對應於文件描述符請求的events結構體。如果POLLIN事件被設置,則文件描述符可以被讀取而不阻塞。如果POLLOUT被設置,則文件描述符可以寫入而不導致阻塞。這些標誌並不是互斥的:它們可能被同時設置,表示這個文件描述符的讀取和寫入操作都會正常返回而不阻塞。

  timeout參數指定等待的毫秒數,無論I/O是否準備好,poll都會返回。timeout指定爲負數值表示無限超時,使poll()一直掛起直到一個指定事件發生;timeout爲0指示poll調用立即返回並列出準備好I/O的文件描述符,但並不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來,立即返回。

  返回值和錯誤代碼
  成功時,poll()返回結構體中revents域不爲0的文件描述符個數;如果在超時前沒有任何事件發生,poll()返回0;失敗時,poll()返回-1,並設置errno爲下列值之一:
  EBADF         一個或多個結構體中指定的文件描述符無效。

  EFAULTfds   指針指向的地址超出進程的地址空間。

  EINTR      請求的事件之前產生一個信號,調用可以重新發起。

  EINVALnfds  參數超出PLIMIT_NOFILE值。

  ENOMEM       可用內存不足,無法完成請求。

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