C/C++ Select Poll Epoll 多路转接
多路转接:
- 并发性:单线程/进程即可同时处理多个I/O操作,无需创建大量线程或进程
- 非阻塞:在监听过程中,程序不会阻塞,仅在检测到就绪事件时才触发处理
- 事件驱动:基于文件描述符的就绪状态(如可读、可写、异常)执行回调或轮询操作
用个通俗易懂的例子举例:一个人钓鱼,掌管着多个鱼竿,哪个鱼竿的浮漂动,才会去拉起钓竿。
多路转接----select与poll与epoll
一.select
1.参数
select可以同时管理和监听多个文件描述符,程序会停在select函数,直到有就绪事件。
nfds----鉴定文件描述符中最大值+1 如 1 4 7三个描述符 nfds应为8
readfds/writefds/exceptfds----确定关心哪些文件描述符的哪些事件--读 写 异常
timeval----
1. NULL--阻塞等待
适用场景:需持续监控I/O事件,无超时需求。
2. 0-非阻塞等待
select
立即返回,仅检查当前描述符状态(轮询模式)
适用场景:高频检测I/O状态,避免程序阻塞。
3. 指定数字 超时模式(timeout > 0
)
在指定时间内等待事件就绪,超时后返回0。例如,设置超时为3秒:
fd_set可以认为是一个bit位图,通过在第几个bit位置确定是哪个文件描述,因此他的大小是有限制的,取决于fd_set,这与内核系统的设置有关。
如readfds 0001101 //就是关心 0,2,3文件描述符的读事件。
然后假如说0文件描述符的读事件就绪,那么select就会返回
原来的0001101会被修改为0000001,其他没就绪的就被置空。
但是对位操作比较麻烦,所以为我们提供了接口。
2.返回值
1.执行成功则返回文件描述词状态已改变的个数
2. 如果返回0代表在描述词状态改变前已超过timeout时间。
3.当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds 和 timeout 的值变成不可预测。
3.总结
缺点:
1.需要自己手动管理文件描述符,在循环监听时,每次都需要手动设置fd集合。
2.每次使用fd集合都需要将其从用户态拷贝到内核态
3.select每次监听,都需要在内核态进行遍历所有fd
4.select能监听的fd有限。
二.Poll
poll与select比较相似。
1.参数
fds---
就相当于将select的readfds/writefds 整合起来。
一个pollfd 中包含了,需要监听文件描述符,与需要监听的事件,和返回就绪事件。
nfds---fds数组的数量
2.返回值
3.总结
缺点:
1.每次内核需要轮询检测哪些事件就绪
2.每次使用fd集合都需要将其从用户态拷贝到内核态
3.随着监听fd的增多,每次可能只有几个fd就绪,效率会随着下降。
优点:
1.不需要在循环设置监听事件,接口使用更方便
2.没有监听事件数量设置(但过多,性能也下降)
三.epoll
1.函数与参数
int epoll_create(int size);
返回一个epoll句柄,size可填可不填,记住如果不需要epoll模型,要close()关闭
作用:在内核创建一个epoll模型
何为epoll模型
简单的概括来说
一个epoll模型中有一个红黑树和一个链表
红黑树有将fd和event封装起来的结点epollitem,每次插入删除通过红黑树进行,所以效率非常的高,其次当事件就绪,与其对应的驱动程序会调用epollitem相应的回调函数,将其加入链表中,
那么链表就可以存储就绪事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd句柄
op操作符
fd需要监听的文件描述符
event这里我们只需要关心events和data.fd与fd设置一样即可 即可
events:
ET模式:文件描述符状态变化
例如读事件从有无数据到有数据 类似电平0到1跳转的那一瞬间才会触发
例如写事件缓冲区空间从无到有 类似电平从1到0跳转的那一瞬间
所以我们必须要一次性将数据读完,
所以应文件描述符设置非阻塞模式,这是因为read阻塞式读取一次可能不会将数据读完,所以我们应采用非阻塞轮询的方式,直到所有数据读完。
LT模式:持续通知
例如只要有数据,就一直在就读队列, 类似电平只要为1就为真。
epoll默认为LT模式。
若是我们想设置ET模式
只需要 事件 | 模式即可
ev.events = EPOLLIN | EPOLLET;
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epfd句柄
events传入要监听的events,epoll会将就绪事件赋值到events数据中
maxevnets是events结构体数组的元素个数
timeout与上述epoll相似
返回值与poll一样
2.优点
1.使用方便,不需要每次再循环设置监听事件,实现输入输出参数分离
2.数据拷贝轻量,只需要add时候加入文件标识符的时候需要拷贝(而其他两个每次循环都要进行拷贝)
3.事件回调机制,当一个事件就绪,不需要循环遍历,只需要调用回调函数,将当前结点直接加入就绪队列。
4.没有文件限制