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

news/2025/2/15 4:16:25/

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

IO 多路复用

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


1. select

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

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • pollfd *fds pollfd结构体数组指针
  • nfds_t nfds 结构体数组指针指向数组的元素个数
  • int timeout 超时时间
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.性能比较

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

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


http://www.ppmy.cn/news/1572144.html

相关文章

【JavaScript爬虫记录】记录一下使用JavaScript爬取m4s流视频过程(内含ffmpeg合并)

前言 前段时间发现了一个很喜欢的视频,可惜网站不让下载,简单看了一下视频是被切片成m4s格式的流文件,初步想法是将所有的流文件下载下来然后使用ffmpeg合并成一个完整的mp4,于是写了一段脚本来实现一下,电脑没有配python环境,所以使用JavaScript实现,合并功能需要安装ffmpeg,…

算法-整理图书,反转链表数据返回

力扣题目:LCR 123. 图书整理 I - 力扣(LeetCode) 书店店员有一张链表形式的书单,每个节点代表一本书,节点中的值表示书的编号。为更方便整理书架,店员需要将书单倒过来排列,就可以从最后一本书…

Go语言的内存分配原理

Go语言的内存分配原理 Go语言的内存管理分为两个主要区域:栈(Stack) 和 堆(Heap)。理解这两个区域的工作原理,可以帮助你写出更高效的代码,并避免一些常见的性能问题。 1. 栈(Stac…

《通过DINO语义引导进行可变形单次人脸风格化》学习笔记

paper:2403.00459 GitHub:zichongc/DoesFS: [CVPR 24] Official repository for Deformable One-shot Face Stylization via DINO Semantic Guidance 目录 摘要 1、介绍 2、相关工作 2.1 人脸风格化 2.2 ViT特征表示 3、方法 3.1 预备知识 3.2 框架 3.3 …

Elasticvue使用总结

用了好多es的可视化客户端,但平时用的最多的是Elasticvue这个浏览器插件。总结一下使用教程。 连接 首页大盘 说明: 节点情况:一共三个节点,三个节点既是master节点又是data节点。(一个节点可以既是master又是data&a…

蓝桥杯篇---串行EEPROM AT24C02

文章目录 前言1. 写字节时序(Byte Write)特点时序步骤1.起始条件(Start Condition)2.发送设备地址(Device Address)3.发送内存地址(Word Address)4.发送数据(Data&#x…

Java全栈项目实战:在线课程评价系统开发

一、项目概述 在线课程评价系统是一款基于Spring Boot Vue3的全栈应用,面向高校师生提供课程评价、教学反馈、数据可视化分析等功能。系统包含Web管理端和用户门户,日均承载10万课程数据,支持高并发访问和实时数据更新。 项目核心价值&…

Windows11+PyCharm利用MMSegmentation训练自己的数据集保姆级教程

系统版本:Windows 11 依赖环境:Anaconda3 运行软件:PyCharm 一.环境配置 通过Anaconda Prompt(anaconda)打开终端创建一个虚拟环境 conda create --name mmseg python3.93.激活虚拟环境 conda activate mmseg 4.安装pytorch和cuda tor…