前言:
poll() 的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。好像poll是Linux独有的吧,不跨平台,select则是跨平台的
select() 对于超时值提供了更好的精度:微秒,而poll是毫秒。
功能
监视并等待多个文件描述符的属性变化,一旦有客服端链接或者通信就会返回
头文件以及参数
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数
参数一fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件,也就是传结构体数组的首地址
参数二nfds: 数组的最大长度, 数组中最后一个使用的元素下标+1 ,内核会轮询检测fd数组的每个文件描述符
参数三timeout: 指定等待的毫秒数,无论 I/O 是否准备好,poll() 都会返回.
接下来了解一下struct pollfd这个结构体
struct pollfd{
int fd; // 文件描述符
short event;// 请求的事件
short revent;// 返回的事件
}
fd:每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示 poll() 监视多个文件描述符。
events:指定监测fd的事件(输入、输出、错误),每一个事件有多个取值,一般我们常用POLLIN,POLLOUT,POLLERR,也可以几个变量或一下,即POLLIN | POLLOUT,如下:
revents:revents 域是文件描述符的操作结果事件,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在 revents 域中返回.
注意:每个结构体的 events 域是由用户来设置,告诉内核我们关注的是什么,而 revents 域是返回时内核设置的,以说明对该描述符发生了什么事件
返回值
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
EBADF:一个或多个结构体中指定的文件描述符无效。
EFAULT:fds指针指向的地址超出进程的地址空间。
EINTR:请求的事件之前产生一个信号,调用可以重新发起。
EINVAL:nfds参数超出PLIMIT_NOFILE值。
ENOMEM:可用内存不足,无法完成请求。
注意点:
poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,
pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。
poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。
poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。
poll优点
-
poll() 不要求开发者计算最大文件描述符加一的大小。
-
poll() 在应付大数目的文件描述符的时候相比于select速度更快
-
它没有最大连接数的限制,原因是它是基于链表来存储的。
poll缺点
-
大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
-
与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符