【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

相关推荐
云空2 小时前
《解锁Netlify:静态网站托管》:此文为AI自动生成
linux·服务器·网络·数据库
Yawesh_best2 小时前
DeepSeek私有化部署与安装浏览器插件内网穿透远程访问实战
运维
23级二本计科3 小时前
TCP 全连接队列 内核层理解socket
服务器·网络·tcp/ip
好好学习O(∩_∩)O4 小时前
[网络][tcp协议]:tcp报头
服务器·网络·tcp/ip
zhonguncle4 小时前
如何在Ubuntu上构建编译LLVM和ISPC,以及Ubuntu上ISPC的使用方法
linux·ubuntu·ispc
蒜白5 小时前
网络工程安全从入门到“入魂“教学案
网络·网络工程师·安全架构
Wizard7975 小时前
在linux 系统下的qt 安装mqtt库
linux·运维·qt
the sun345 小时前
NAT、代理服务器、内网穿透和内网打洞
网络·智能路由器
Python破壁人手记5 小时前
《我的Python觉醒之路》之转型Python(十五)——控制流
java·服务器·开发语言·网络·python
Michaelwubo6 小时前
jenkins 配置邮件问题整理
java·运维·jenkins