多路复用——poll

一、基本知识
web

  poll的机制与select相似,与select在本质上没有多大差异,管理多个描述符也是进行轮询,根据描述符的状态进行处理,可是poll没有最大文件描述符数量的限制。poll和select一样存在一个缺点就是,包含大量文件描述符的数组被总体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增长而线性增大。数组

二、poll函数ide

  函数格式以下所示:函数

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

pollfd结构体定义以下:spa

struct pollfd {指针

int fd;         /* 文件描述符 */
short events;         /* 等待的事件 */
short revents;       /* 实际发生了的事件 */
} ; 
orm

  每个pollfd结构体指定了一个被监视的文件描述符,能够传递多个结构体,指示poll()监视多个文件描述符。每一个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操做结果事件掩码,内核在调用返回时设置这个域。events域中请求的任何事件均可能在revents域中返回。合法的事件以下:blog

  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       可用内存不足,没法完成请求。


三、经过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

四、poll的特色 

a)poll把文件描述符和事件绑定,而且事件能够单独指定,而且能够是多个事件的按位或,这样更加细化了事件的注册,并且poll单独采用一个元素用来保存就绪返回时的结果,这样在下次调用poll时,就不用重置以前注册的事件; 
b)poll采用对全部注册的文件描述符集轮询的方式,会返回整个用户注册的事件集合,因此应用程序索引就绪文件的时间复杂度为O(n)。 
c)poll用nfds参数指定最多监听多少个文件描述符和事件,这个数能达到系统容许打开的最大文件描述符数目,即65535。 
d)只能工做在LT模式。

五、poll的优势

1)poll与select不一样,经过一个pollfd数组向内核传递须要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只须要被初始化一次。 

2)poll的实现机制与select相似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,而后对pollfd中的每一个描述符进行poll,相比处理fdset来讲,poll效率更高。

六、poll的缺点

1)虽然fd没有限制,可是事实上,同事链接的客户端在时刻可能只有不多的处于就绪状态,所以随着监视的描述符数量的增加,其效率也会线性降低。 

2)每次调用poll,存在由用户到内核的拷贝,开销大

3)每次调用poll,都要轮询检测events数组中fd的revents是否是和其events相同,开销大

相关文章
相关标签/搜索