【Linux】【网络】IO多路复用 select、poll、epoll

【Linux】【网络】IO多路复用 select、poll、epoll

IO 多路复用

进程或线程同时监控多个文件描述符,查看描述符上是否有事件发生,从而提高资源利用率和系统吞吐量。


1. select

cpp 复制代码
int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti
meval *timeout);
  • 返回值代表多少个文件描述符上有事件

  • int maxfd 被监听的文件描述符的总数

  • fd_set *readfds 读事件

  • fd_set *writefds 写事件

  • fd_set *exceptfds 异常事件

  • struct timeval *timeout 超时时间

  • 基本原理

    • 使用固定大小的 fd_set(集合类型),每个 代表一个文件描述符。 最多能够监听1024个文件描述符
    • 每次使用时,应用程序都需要将需要监控的文件描述符添加到fd_set集合中,然后调用系统调用 select()。
    • 内核在超时时间内轮询 fd_set,当检测到某个文件描述符发生指定的 I/O 事件select只有可读、可写、异常)时,返回就绪的文件描述符集合。
  • 工作流程

    1. 应用程序将需要监控的 fd 添加到 fd_set 通过**FD_SET()**设置对应的位中,并设置超时时间。
    2. 调用 select() 后,内核遍历整个 fd_set,检查每个文件描述符的状态。
    3. 如果有文件描述符处于就绪状态,select() 返回 fd_set中有事件的位为1 通过**FD_ISSET()**轮询检测;否则在超时后返回

2.poll

cp 复制代码
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • pollfd *fds pollfd结构体数组指针
  • nfds_t nfds 结构体数组指针指向数组的元素个数
  • int timeout 超时时间
cpp 复制代码
struct pollfd
{
int fd; // 文件描述符
short events; // 注册的关注事件类型  每一位可以代表一个类型 因此最多检测16个事件
short revents; // 实际发生的事件类型,由内核填充
};
  • fd 成员指定文件描述符
  • events 成员告诉 poll 监听 fd 上的哪些事件类型。它是一系列事件的按位&
  • revents 成员则由内核修改,通知应用程序 fd 上实际发生了哪些事件
  • 基本原理
    • poll 使用一个 pollfd 数组来描述 需要监控的文件描述符及其关注的事件, 结构体数组可以开辟的很大,不受1024的限制
    • 事件类型可以更多
    • 事件和描述符封装在一起
  • 工作流程
    1. 应用程序填充一个 pollfd 数组,每个元素记录文件描述符和关注的事件(如 POLLIN、POLLOUT)。
    2. 调用 poll() 后,内核遍历数组,检查每个 fd 的状态。
    3. poll() 返回就绪的文件描述符个数,并通过 pollfd 数组的 revents 字段告知应用程序哪些事件发生了。

3.epoll

  • epoll 使用一组函数来完成任务,epoll 把用户关心的文件描述符上的事件放在内核里的一个事件表中。从而无需像 select 和 poll 那样每次调用都要重复传入文件描述符或事件集。

  • 但 epoll 需要使用一个额外的文件描述符,来唯一标识内核中的这个内核事件表

  • epoll_create()用于创建内核事件表

  • epoll_ctl()用于操作内核事件表

  • epoll_wait()用于在一段超时时间内等待一组文件描述符上的事件 //可能阻塞

  • 基本原理

    • epoll 是 Linux 特有的机制,其设计目标是高效处理大规模文件描述符。
    • epoll 将所有注册的文件描述符存储在内核内部的数据结构中(通常采用红黑树管理所有注册项),并维护一个就绪队列用于保存触发事件的 fd。
    • 应用程序通过 epoll_ctl() 添加、修改或删除文件描述符,通过 epoll_wait() 获取就绪事件。
  • 数据结构

    • 红黑树(rbtree):存储所有已注册的文件描述符(epitem),便于快速查找、添加和删除操作,时间复杂度 O(log N)。
    • 就绪链表(ready list):存储已经触发事件的 epitem,当调用 epoll_wait() 时,内核直接返回这个就绪链表中记录的文件描述符,时间复杂度与就绪 fd 数量相关,通常远小于所有注册 fd 数量。
  • 支持两种触发模式:

    • 水平触发(Level Triggered, LT):类似 poll,每次调用 epoll_wait() 都返回当前就绪的 fd。
    • 边缘触发(Edge Triggered, ET):仅在状态变化时通知,必须在一次性读取完数据,否则不会重复通知。

4.性能比较

特性 select poll epoll
文件描述符数量限制 受 FD_SETSIZE 限制(通常 1024 个) 无固定限制,但效率随数组长度线性下降 支持大量文件描述符,效率依赖于就绪事件数量
每次检查是否需要重新将数据拷贝给内核
扫描方式 线性遍历整个 fd_set 线性遍历 pollfd 数组 内核通过红黑树查找 + 就绪链表遍历
内核检测就绪描述符时间复杂度 O(N) O(N) O(1)
查找就绪描述符时间复杂度 O(N) O(N) O(1)
触发模式 仅支持水平触发 仅支持水平触发 支持水平触发和边缘触发
平台支持 POSIX 标准,跨平台支持 POSIX 标准,跨平台支持 仅 Linux 支持

后续会详细写一下epoll 每个函数 以及内核实现方式 因为这个比较重要 以及libevent底层也是epoll

相关推荐
深圳特力康何哈哈23 分钟前
输电杆塔沉降智能监测系统:如何用数据守护电网安全
运维·安全
nandao15827 分钟前
nginx通过location配置代理的原理和方式
运维·nginx
kk努力学编程28 分钟前
Linux基础18-C语言篇之运算符Ⅰ【入门级】
linux·运维·c语言
爱吃喵的鲤鱼1 小时前
tcp传输协议机制
网络·网络协议·tcp/ip
NicOym1 小时前
Linux(socket网络编程)TCP连接
linux·c++
Ruimin05193 小时前
Mysql8.0使用PXC
运维
luoqice5 小时前
嵌入式linux下如何通过IIS接口驱动实现录制pcm音频
linux
陈老师还在写代码5 小时前
在服务器部署JVM后,如何评估JVM的工作能力,比如吞吐量
运维·服务器·jvm
handsomestWei5 小时前
k8s优雅操作pod容器组
运维·云原生·k8s·pod
Karoku0665 小时前
【k8s应用管理】kubernetes 存储管理
运维·docker·云原生·容器·kubernetes