IO多路复用中 select、poll、epoll

本文详细介绍了IO多路复用技术,包括select、poll和epoll三种方法的原理及优缺点。通过对比不同方法的特点,帮助读者理解如何选择合适的IO多路复用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IO多路复用中 select、poll、epoll

在这里插入图片描述

什么是IO多路复用?

IO多路复用就是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。

一般来说,我们通常是一个请求就是一个线程,这样会造成多个线程同时存在,会造成上下文的切换造成不必要的开销。IO多路复用是指一个线程来管理这一个个的请求。

文件句柄博客


实现原理

select 方法

原理

在这里插入图片描述

虚线上面的代码就是找到文件描述符的集合以及最大的文件描述符。

虚线下面的代码中,首先是初始化一个 rset ,它的实质是一个bitamap 数组,默认为 1024 位

如果 fds 的值为 1、2、5、7、9,那么 bitmap 的值就会是 011001010100…

下面就是一个 select(max+1,&rset,null,null,null) 方法,第一个参数是为了卡位,减小遍历的范围,第二个是我们标记的bitmap数组(读文件描述集合),第三个参数是写文件描述集合和第四个参数异常文件描述集合,第五个参数是超时时间。

在这里插入图片描述

上面图片就是select方法的运行图,它会先把rset拷贝到内核中,然后对其进行轮询操作,如果有数据可读则将对应的位(rset中)进行置位并且退出返回,如果没有数据则进行阻塞。遍历文件描述符集合(与rset做对比),找出有数据的那个数据操作。

为什么要在内核态中进行轮询比较呢?

因为用户态的轮询也是要切换到内核态的,这样会造成更多的上下文切换,浪费更多的时间,所以干脆就全部都在内核态中完成。

优点
  • 将遍历文件描述符找出有数据的连接操作交给操作内核,减少用户态和内核态的切换次数,提高了速度。
缺点
  • bitmap有大小限制1024。
  • FDset不可重用,rest在操作内核中被修改置位了,所以每次都要重新初始化。
  • 每次都需要重新将 rset 拷贝进入操作内核,有一定开销。
  • 对于有数据的文件描述符,需要再次进行O(n)遍历。

poll 方法

原理

在这里插入图片描述
在这里插入图片描述

我们可以看到整个rset没有了,变成了一个结构体 ——> pollfd。

fd:文件描述符的标识符

events:关注事件 pollin读 和 pollout写

revent:对event的一个回馈(初始为0),如果有可读情况则变成 1

在这里插入图片描述

poll方法做的事其实和select差不多,只是遍历的对象变成了pollfd集合而已,在poll中如果有数据则将其对应的revent置位1 ,然后方法返回,在遍历操作pollfds集合时,将revent重新赋值为0(实现了复用)。

优点
  • 大部分和select相同,但是解决了select bitmap大小限制问题和fdset不可重用问题
  • 由于是数组,所以解决了最大值限制 1024
  • 去除了 FDset ,实现了复用。
缺点
  • 每次都需要重新将 rset 拷贝进入操作内核,有一定开销。
  • 对于有数据的文件描述符,需要再次进行O(n)遍历(因为它不知道哪个pollfd发生了变化)。

epoll 方法

在这里插入图片描述
在这里插入图片描述

就是在用户态和内存态之间采用结构体(红黑树和双向链表),然后将事件都注册进去,并且注册一个回调事件,当有数据可读时就将其添加到双线链表中(重排),并且退出返回数量n。然后只需要遍历这个文件描述符的前n个就行。

优点
  • 大部分和select相同,但是解决了select bitmap大小限制问题和fdset不可重用问题
  • 由于是数组,所以解决了最大值限制 1024
  • 去除了 FDset ,实现了复用。
  • 采用了共同持有的空间,避免了内存拷贝的消耗
  • 对于有数据的文件描述符,只需要遍历前n个就行。

### 异步IO多路复用selectpollepoll的区别 #### Select机制分析 Select采用的是轮询的方式去检测每一个文件描述符的状态变化,当调用`select()`时,它会阻塞直到有一个或多个文件描述符准备好进行读取或写入操作,或者是超时发生。然而,在每次调用之前都需要重新设置感兴趣的文件描述符集合,并且对于大量连接来说效率较低,因为其内部实现是对所有传入的文件描述符依次做线性扫描。 #### Poll机制解析 PollSelect相似之处在于两者都基于水平触发模式工作,这意味着只要某个文件描述符处于可读状态,则一直保持激活直至被消费掉为止。但是poll解决了select的最大文件描述符数量受限于FD_SETSIZE的问题,理论上支持更大的并发量。不过poll同样存在性能瓶颈—随着监控列表的增长,遍历整个数组查找已准备好的事件仍然是一项耗时的工作[^4]。 #### Epoll特性介绍 Epoll则采用了完全不同的设计理念来优化上述两种方式存在的缺陷: - **边缘触发(Edge Triggered, ET) vs 水平触发(Level Triggered, LT)**: 支持这两种模式,默认情况下为LT模式下运作。ET模式仅在监测到新的数据到来瞬间触发一次回调通知给应用层;而在LT模式里只要有未处理的数据就会持续报告。 - **高效的通知机制**: 不同于前两者的被动查询模型,epoll主动告知哪些具体的socket已经发生了特定类型的活动,从而使得开发者能够精准定位并响应感兴趣的变化源而不必逐一排查全部可能的对象。 - **高性能扩展能力**: 利用了Linux内核提供的红黑树结构以及hash表技术实现了高效的动态增删改查功能,这不仅提高了系统的吞吐率还降低了CPU占用率。此外,由于只关心活跃链接所以即使面对海量级的同时在线用户也能维持良好的表现力[^1]。 ```c int epoll_fd = epoll_create(EPOLL_SIZE); struct epoll_event event; event.events = EPOLLIN | EPOLLET; // 设置为边沿触发 event.data.fd = listen_sock; // 注册监听套接字至epoll实例中 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1){ perror("epoll set insertion error"); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值