poll函数工作原理与select函数类似,也是监管一系列的文件描述符,阻塞的去轮询看这些文件描述符是否可读/可写/异常,再去调用io函数读写。
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 注册的事件 */
short revents; /* 实际发生的事件,由内核填充 */
};
(1)fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,
它指向所有我们感兴趣的文件描述符上发生的事件
(2)nfds:表示fds结构体数组的长度
(3)timeout:表示poll函数的超时时间,单位是毫秒
返回值:
(1)小于0,表示出错
(2)等于0,表示poll函数等待超时
(3)大于0,表示poll由于监听的文件描述符就绪返回,并且返回结果就是就绪的文件描述符的个数。
poll支持的事件类型如下图所示
特点分析:
1、优点
(1)poll不要求开发者计算最大文件描述符加一的大小。
(2)poll在应付大数目的文件描述符的时候速度更快,相比于select。
(3)它没有最大连接数的限制,原因是它是基于链表来存储的。
(4)在调用函数时,只需要对参数进行一次设置就好了
2、缺点
(1)大量的句柄数据结构被整体复制于用户态和内核地址空间之间,产生巨大的开销;
(2)poll采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;
(3)同时连接的大量客户端在一时刻可能只有很少的就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降
(4)返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;
(5)触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程。
示例代码
#include<poll.h>
#include<signal.h>
#include<iostream>
#include <unistd.h>
using namespace std;
int main()
{
/*第一步 poll开始监听之前要知道要监管哪些套接字*/
struct pollfd fds[1];
fds[0].fd=0;
fds[0].events=POLL_IN;
fds[0].revents=POLL_IN;
/*第二步 poll开始工作 阻塞的轮询看监管的套接字是否就绪*/
int ret=poll(fds,1,5000);
/*第三步 poll完成工作 有套接字就绪或者时间超时返回*/
if(ret<0)
{
cout<<"error"<<endl;
}
else if(ret==0)
{
cout<<"time out"<<endl;
}
else
{
if(fds[0].revents==POLL_IN)
{
char message[10];
read(fds[0].fd,message,sizeof(message));
cout<<message<<endl;
}
}
}
上面的代码实现的功能就是把标准输入(即文件描述符为0)纳入poll的监管,然后poll在5s内阻塞的轮询看是否有读就绪事件。如果有的话就返回对其进行处理,如果超时或者出错的话也返回