多路複用——poll

1、基本知識

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

2、poll函數

  函數格式如下所示:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

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中返回。待監聽的事件由events成員指定,函數在相應的revents成員中返回該描述符的狀態(每個文件描述符都有兩個事件,一個是傳入型的events,一個是傳出型的revents,從而避免使用傳入傳出型參數,注意與select的區別),從而告知應用程序fd上實際發生了哪些事件。events和revents都可以是多個事件的按位或。

  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       可用內存不足,無法完成請求。


3、通過poll向stdin輸入數據並回顯,timeout設置爲5000ms

代碼:

#include <stdio.h>
#include <poll.h>
int main()
{
	struct pollfd fd_ev[1];
	fd_ev[0].fd = 0;
	fd_ev[0].events = POLLIN;
	fd_ev[0].revents = 0;
	int done = 0;
	while(!done){
		int timeout =5000;
		switch( poll(fd_ev, 1, timeout) ){
			case -1:
				perror("poll");
				break;
			case 0:
				printf("timeout...\n");
				break;
			default:
				{
				if(fd_ev[0].revents & POLLIN){
				  char buf[1024];
			          ssize_t _s = read(fd_ev[0].fd, buf, sizeof(buf)-1);
						if( _s > 0 ){
							buf[_s-1] = '\0';
							printf("echo: %s\n", buf);
						}
					}
				}
				break;
		}
	}
}

wKioL1ee8M3D9RrSAAA_vPkN_Y0554.png

4、poll的特點 

a)poll把文件描述符和事件綁定,並且事件可以單獨指定,並且可以是多個事件的按位或,這樣更加細化了事件的註冊,而且poll單獨採用一個元素用來保存就緒返回時的結果,這樣在下次調用poll時,就不用重置之前註冊的事件; 
b)poll採用對所有註冊的文件描述符集輪詢的方式,會返回整個用戶註冊的事件集合,所以應用程序索引就緒文件的時間複雜度爲O(n)。 
c)poll用nfds參數指定最多監聽多少個文件描述符和事件,這個數能達到系統允許打開的最大文件描述符數目,即65535。 
d)只能工作在LT模式。

5、poll的優點

1)poll與select不同,通過一個pollfd數組向內核傳遞需要關注的事件,故沒有描述符個數的限制,pollfd中的events字段和revents分別用於標示關注的事件和發生的事件,故pollfd數組只需要被初始化一次。 

2)poll的實現機制與select類似,其對應內核中的sys_poll,只不過poll向內核傳遞pollfd數組,然後對pollfd中的每個描述符進行poll,相比處理fdset來說,poll效率更高。

6、poll的缺點

1)雖然fd沒有限制,但是事實上,同事連接的客戶端在時刻可能只有很少的處於就緒狀態,因此隨着監視的描述符數量的增長,其效率也會線性下降。 

2)每次調用poll,存在由用戶到內核的拷貝,開銷大

3)每次調用poll,都要輪詢檢測events數組中fd的revents是不是和其events相同,開銷大

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