I/O复用 poll简介
1、基本概念
poll起源于SVR3,开始时局限于流设备,在SVR4时取消了此限制,允许poll工作在任何描述符上,但涉及到流设备时,它还提供了附加信息。
poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
2.poll函数
# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout); 返回:准备好的描述字个数,0-超时,1-出错。
参数说明
- fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
- nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
- timeout:poll函数调用阻塞的时间,单位是MS(毫秒),无论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 可用内存不足,无法完成请求。
pollfd结构体定义如下,【在源码文件poll.h文件中】
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 描述字不是一个打开的文件。
3.poll应用实例
服务器端代码:
/* include fig01 */ #include "unp.h" #include <limits.h> /* for OPEN_MAX */ int main(int argc, char **argv) { int i, maxi, listenfd, connfd, sockfd; int nready; ssize_t n; char buf[MAXLINE]; socklen_t clilen; struct pollfd client[OPEN_MAX]; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); client[0].fd = listenfd; client[0].events = POLLRDNORM; for (i = 1; i < OPEN_MAX; i++) client[i].fd = -1; /* -1 indicates available entry */ maxi = 0; /* max index into client[] array */ /* end fig01 */ /* include fig02 */ for ( ; ; ) { nready = Poll(client, maxi+1, INFTIM); if (client[0].revents & POLLRDNORM) { /* new client connection */ clilen = sizeof(cliaddr); connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); #ifdef NOTDEF printf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen)); #endif for (i = 1; i < OPEN_MAX; i++) if (client[i].fd < 0) { client[i].fd = connfd; /* save descriptor */ break; } if (i == OPEN_MAX) err_quit("too many clients"); client[i].events = POLLRDNORM; if (i > maxi) maxi = i; /* max index in client[] array */ if (--nready <= 0) continue; /* no more readable descriptors */ } for (i = 1; i <= maxi; i++) { /* check all clients for data */ if ( (sockfd = client[i].fd) < 0) continue; if (client[i].revents & (POLLRDNORM | POLLERR)) { if ( (n = read(sockfd, buf, MAXLINE)) < 0) { if (errno == ECONNRESET) { /*4connection reset by client */ #ifdef NOTDEF printf("client[%d] aborted connection\n", i); #endif Close(sockfd); client[i].fd = -1; } else err_sys("read error"); } else if (n == 0) { /*4connection closed by client */ #ifdef NOTDEF printf("client[%d] closed connection\n", i); #endif Close(sockfd); client[i].fd = -1; } else Writen(sockfd, buf, n); if (--nready <= 0) break; /* no more readable descriptors */ } } } } /* end fig02 */