一、poll系统调用
poll 和select类似,是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。
poll的原型为:
#include <poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);
返回值:
成功时,返回pollfd结构体中域revents不为0的文件描述描述符的个数(即数组fds中准备好读、写或出错状态的那些socket描述符的总数量);如果在超时前没有事件发生,则返回0;
失败,返回-1。
1),fds参数是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。pollfd结构体定义为:
struct pollfd
{
int fd; //文件描述符
short events; //注册的事件
short revents; //实际发生的事件,由内核填充
}
fd成员指定文件描述符;
events成员告诉poll监听fd上的哪些事件,它是一系列事件的按位或;
revents成员则由内核修改,已通知应用程序fd上实际发生了哪些事件。
poll支持监控的事件类型如下:
事件 | 描述 | 是否可作为输入 | 是否可作为输出 |
POLLIN | 数据(包括普通数据和优先数据)可读 | 是 | 是 |
POLLRDNORM | 普通数据可读 | 是 | 是 |
POLLRDBAND | 优先级带数据可读(linux不支持) | 是 | 是 |
POLLPRI | 高优先级数据可读,比如TCP带外数据 | 是 | 是 |
POLLOUT | 数据(包括普通数据和优先数据)可写 | 是 | 是 |
POLLWRNORM | 普通数据可读写 | 是 | 是 |
POLLWRBAND | 优先级带数据可写 | 是 | 是 |
POLLRDHUP | TCP连接被对方关闭,或者对方关闭了写操作。它由GNU引入 | 是 | 是 |
POLLERR | 错误 | 否 | 是 |
POLLHUP | 挂起。比如管道的写端被关闭后,读端描述符上将收到POLLHUP事件 | 否 | 是 |
POLLNVAL | 文件描述符没有打开 | 否 | 是 |
#define POLLIN 0x0001
#define POLLPRI 0x0002
#define POLLOUT 0x0004
#define POLLERR 0x0008
#define POLLHUP 0x0010
#define POLLNVAL 0x0020
#define POLLRDNORM 0x0040
#define POLLRDBAND 0x0080
#define POLLWRNORM 0x0100
#define POLLWRBAND 0x0200
#define POLLMSG 0x0400
#define POLLREMOVE 0x1000
#define POLLRDHUP 0x2000
2),nfds参数指定被监听事件集合fds的大小、其类型nfds_t的定义如下:
typedef unsigned long int nfds_t;
3),timeout参数指定poll的超时值,单位是毫秒。
当timeout为-1时,poll调用将永远阻塞i,直到某个事件发生;
当timeout为0时,poll调用将立即返回;
当timeout大于0时,为超时时间。
二、poll和epoll在使用上的区别
1),如何索引poll返回的就绪文件描述符
int ret = poll(fds, MAX_EVENT_NUMBER, -1);
//必须遍历所有已注册文件描述符并找到其中的就绪者(当然也可用ret来优化)
for(int i = 0; i < MAX_EVENT_NUMBER; ++i)
{
//判断第i个文件描述符是否就绪
if(fds[i].revents & POLLIN)
{
int sockfd = fds[i].fd;
//处理sockfd
}
}
2), 如何索引epoll返回的就绪文件描述符
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUBER, -1);
//仅遍历就绪的ret个文件描述符
for(int i = 0; i < ret; i++)
{
int sockfd = events[i].data.fd;
//sockfd肯定就绪,直接处理
}