C/C++ Select Poll Epoll 多路转接

发布于:2025-05-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

多路转接:

  • 并发性​​:单线程/进程即可同时处理多个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.没有文件限制


网站公告

今日签到

点亮在社区的每一天
去签到