IO多路转接select、poll、epoll模型

本文介绍I/O多路转接技术中的select、poll及epoll三种方法。重点讲解了它们的工作原理、优缺点及应用场景。select适用于少量文件描述符监控;poll改进了select的固定数量限制;epoll则进一步优化,特别适用于处理大批量文件描述符。

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

I/o多路转接之select:

一、select:

1、特点:负责等
2、可以等待多个fd(read负责读取)
系统提供select函数来实现多路复用输入/输出模型
输入:用户想告诉操作系统哪个是你应该关心的读事件
输出:操作系统告诉用户你所关心的文件描述符上的事件哪些是fd已经就绪

二、select函数原型:

#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

这里写图片描述

三、理解select执行过程:(理解fd_set)

fd_set长度为1字节,fd_set中的每一bit可以对应一个fd,则1字节长的fd_set最大可以对应8个fd。

用int数组来保存文件描述符

四、socket就绪条件:

1、读就绪

1)、Socket内核中接受缓冲区的字节数>=低水位标记SO_RCVLOWAT,可以无阻塞的读

2)、监听的Socket上有新的链解请求

3)、socket上有未处理的错误
4)、Socket Tcp通信中,对端关闭连接,此时对该Socket读,则返回0;

2、写就绪:

1)、Socket内核中,发送缓冲区的可用字节数》=低水位标记SO_RCVLOWAT,此时可以无阻塞的写
2)、Socket的写操作被关闭(close或者shutdown),对一个写操作被关闭的Socket进行写操作,会触发SIGPIPE信号
3)、Socket使用非阻塞connect连接成功或失败后
4)、Socket上有未读取的错误

3、异常就绪:Socket上收到带外数据。(有一个紧急指针的字段)

五、select的特点:

1)可监控的文件描述符个数取决与sizeof(fdset)的值,例:sizeof(fdset)=512,每bit表示一个文件描述符,则此服务器上最大的文件描述符是512*8=4096.
2)fd加入select监控集,再使用一个数组array保存放到select监控集中的fd
*用于再select返回后,array作为源数据和fdset进行DISSSET判断
*****Select返回后把以前加入的但并无事件发生的fd清空,每次开始select前都必须从array数组取得fd逐一加入,扫描array的同时取得fd最大值maxfid,用于select的第一个参数。

六、selec的缺点:

1)性能降低,代码难写

2)数组承载有上限(位图有大小,所存的文件描述符就有限)

3)开销大

4)输入输出函数混合在一起必须依赖一个第三方数组

I/O多路转接之poll:

一、函数接口:

#include<poll.h>
int poll(struct pollfd *fds,nfds_t nfds,int timeout);
//pollfd 结构
struct pollfd{
     int fd;                     /*file descriptor*/
     short events;       /*requested events*/
     short revents;     /*returned events*/
     };

这里写图片描述

2、events和revents的取值

events:所关心的fd是什么

revents:所关心的fd哪些已经就绪

3、返回结果:

1)返回值小于0,表示出错
2)返回值等于0,表示poll函数等待超时;
3)返回值大于0,表示poll由于监听的文件描述符就绪而返回。

二、socket就绪条件:

同select

三、poll的优点:

不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现

1)polled结构包含了要发生的event和发生过的event,不再使用select”参数-值”的传递方式,

接口比select更方便

2)poll并没有最大数量限制

(但是数量过大后性能也是会下降)

四、poll的缺点

poll监听的文件描述符数目增多时:

1)性能下降

(同时连接的大量客户端一时刻只有少数处于就绪状态)

2)麻烦

(每次调用poll都要把大量的pollfd结构从用户态拷到内核中)
(poll返回后,需要轮询polled来获取就绪的描述符)

I/o多路转接之epoll

一、epoll初识:是为处理大批量句柄而做了改进的poll

二、epoll工作原理:

这里写图片描述

三、epoll的三个调用:

1、epoll_create

int epoll_create(int size);
创建一个epoll的句柄。
1)linux2.6.8之后,size参数是被忽略的
2 )用完之后,必须调用close()关闭。

2、epoll_ctl(操控红黑树)

    int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);

这里写图片描述

3、epoll_wait(操控就绪队列)

int epoll_wait(int epfd,struct epoll epoll_event * events,int maxevents,int timeout);

这里写图片描述

总结:

epoll使用三部曲:

1)调用epoll_create创建一个epoll句柄
2)调用epoll_ctl,将要监控的文件描述符进行注册
3)调用epoll_wait,等待文件描述符就绪

四、epoll的优点

1)文件描述符无上限
2)基于事件的就绪通知方式。(函数的读和写分开来执行)(一旦就绪,内核采用callback的回调机制,迅速激活这个文件描述符,不会影响性能)
3)维护就绪队列:当fd就绪,就被放入就绪队列,调用epoll_wait获取fd(O(1))
4)内存映射机制错误(建立过程中传了缓冲区,完成了从内核到用户数据的拷贝)

五、epoll工作方式:默认(LT)

水平触发(LT)&&边缘触发(ET)

LT:可靠,不会丢失数据
ET:更高效 有->无 才通知
少->多

LT:

1)既可用也可不用非阻塞

2)检测到socket事件时,不立刻处理,或只处理一部分
3)读1k数据,缓冲区还剩1k,在第二次调用epoll_wait时,仍然立刻返回并通知socket读事件就绪
4)data被处理完,epoll_wait才不会立刻返回

ET:

1)遇到socket事件就绪,必须立刻处理
2)读1k数据,缓冲区还剩1k,在第二次调用epoll_wait时,不会再返回
3)fd就绪后,只有一次处理机会
4)ET比LT性能更高(epoll_wait返回的次数少了很多)。

5)只支持非阻塞的读写:数据就绪只会通知一次,ET:数据就绪,需一直read,直到出错或完成为止。

六、epol使用场景

对于多连接,且多连接中只有一部分连接比较活跃时,比较适合用epoll。(QQ,微信)

七、epoll中的惊群问题

本来连接很多活跃度低,但是在某一时刻,活跃度骤升,就会导致epoll服务器压力过大。

### 异步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、付费专栏及课程。

余额充值