IO——poll 函数
文章目录
- 函数原型
- fds
- nfds
- timeout
- 返回值
- IO 流程
- 使用示例
在单个线程中,select 函数能够监视的文件描述符数量有最大的限制,一般为 1024,可以修改内核将监视的文件描述符数量改大,但是这样会降低效率!
这个时候就可以使用 poll 函数,poll 函数本质上和 select 没有太大的差别,但是 poll 函数没有最大文件描述符限制,
函数原型
linux 应用程序中 poll 函数原型如下所示
#include<poll.h>int poll(struct poll_fd *fds, nfds_t nfds, int timeout)
fds
要监视的文件描述符集合以及要监视的事件,为一个数组,数组元素都是结构体 struct pollfd 类型
// pollfd结构
struct pollfd {int fd; /* 要监听的fd */short int events; /* 要监听的事件类型:读、写、异常 */short int revents;/* 实际发生的事件类型 */
};
- fd 文件描述符,如果 fd 无效的话,那么 events 监视事件也就无效,并且 revents 返回 0
- events 要监视的事件,可监视的事件类型如下所示
| 事件 | 描述 | 是否可作为输入(events) | 是否可作为输出(revents) |
|---|---|---|---|
| POLLIN | 数据可读 | 是 | 是 |
| POLLOUT | 数据可写 | 是 | 是 |
| POLLPRI | 高优先级数据可读 | 是 | 是 |
| POLLRDNORM | 普通数据可读 | 是 | 是 |
| POLLERR | 指定的文件描述符发送错误 | 否 | 是 |
| POLLNVAL | 无效的请求 | 否 | 是 |
- revents 返回的事件,由 linux 内核设置具体的返回事件
nfds
poll 函数要监视的文件描述符数量
timeout
timeout表示超时时间(以毫秒为单位)。
返回值
返回 revents 域中不为 0 的 pollfd 结构体个数,也就是发生事件或错误的文件描述符数量;0,超时;-1,发生错误,并且设置 errno 为错误类型
IO 流程
- 创建pollfd数组,向其中添加关注的fd信息,数组大小自定义
- 调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
- 内核遍历fd,判断是否就绪
- 数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
- 用户进程判断n是否大于0
- 大于0则遍历pollfd数组,找到就绪的fd
通过上述流程,我们发现相对于select,poll有了两点改变:
1、select模式中的fd_set大小固定为1024,而pollfd在内核中采用链表,理论上无上限
2、监听FD越多(fd_set越大),每次遍历消耗时间也越久,性能反而会下降
虽然poll解决了select中监听fd的上限,但是poll中还是要遍历所有的FD,且如果fd监听过多会导致性能下降。
使用示例
#define NFDS 100void DealFinishFd(struct pollfd *fds)
{for (unsigned int i = 0; i < NFDS; i++){if (fds[i].fd == -1){continue;}if (fds[i].revents & POLLIN){}else if (fds[i].revents & POLLRDHUP){}else if (fds[i].revents & POLLIN){}}
}int main(void)
{struct pollfd *fds = (struct pollfd *)malloc(sizeof(struct pollfd) * NFDS);assert(NULL != fds);InitFds(fds);while (1){int n = poll(fds, NFDS, -1);if (n <= 0){printf("poll error\n");continue;}DealFinishFd(fds);}free(fds);
}