1 什么是多路复用

        多路复用(Multiplexing) 是一种让单个线程同时处理多个 I/O 通道的技术,核心是通过系统调用将 I/O 状态查询的工作交给操作系统内核,应用程序只需等待内核通知哪些通道就绪。

  • 多路:指的是多个socket网络连接
  • 复用:指的是复用一个线程,使用一个线程来检查多个文件描述符(Socket)的就绪状态
  • 多路复用主要使用的是三种技术:select、poll、epoll。epoll是最新的,也是目前最好的多路复用

2 三种模式的对比

特性selectpollepoll
数据结构固定大小位图(FD_SETSIZE)动态链表红黑树 + 就绪链表
FD 数量限制通常 1024无限制无限制
事件通知机制遍历所有 FD遍历所有 pollfd回调函数直接加入就绪列表
时间复杂度O(n)O(n)O(1)
用户 / 内核空间复制每次调用复制整个 FD 集合每次调用复制整个 pollfd 数组仅注册时需要复制
触发模式仅支持水平触发(LT)仅支持水平触发(LT)支持水平触发(LT)和边缘触发(ET)

3 select

3.1 基本概念

select 是 Linux 系统中最早的 I/O 多路复用机制,它使用位图(fd_set)来表示要监控的文件描述符集合。用户空间调用 select 方法后,通过系统调用进入内核,内核会遍历位图中设置为 1 的每一位,对每个对应的文件描述符调用其 poll 方法来检查是否有数据可读、可写或发生异常。当有事件就绪时,内核会将结果位图拷贝回用户空间,用户程序通过检查结果位图来确定哪些文件描述符发生了事件

3.2 实现机制

  • 数据结构:使用固定大小的位图(bitmap)存储文件描述符集合,默认最大支持 1024 个 FD。
  • 调用流程
    1. 用户程序创建 fd_set 并添加关注的 FD
    2. 调用 select() 将 fd_set 从用户空间复制到内核空间
    3. 内核遍历所有 FD,检查是否有就绪事件
    4. 将就绪状态写入 fd_set 并复制回用户空间
    5. 用户程序遍历 fd_set 检查哪些 FD 就绪
  • 关键缺点
    • FD 数量限制(通常 1024)
    • 每次调用需复制整个 fd_set
    • 遍历检查就绪状态的时间复杂度为 O (n)

3.3 select源码分析

3.3.1 关键数据结构

// include/uapi/asm-generic/select.h
typedef struct {unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} fd_set;// fs/select.c
struct fdtable {unsigned int max_fds;     // 最大文件描述符数fd_set *open_fds;         // 打开的文件描述符集合fd_set *close_on_exec;    // 执行exec时关闭的集合fd_set *reserve_fds;      // 保留集合
};

3.3.2 核心调用链

sys_select()              // 用户调用的系统调用-> core_sys_select()    // 核心处理函数-> do_select()        // 执行select逻辑-> poll_initwait()  // 初始化等待队列-> f_op->poll()     // 调用每个文件描述符的poll方法-> __pollwait()     // 将当前进程添加到等待队列-> check_events()   // 检查事件是否就绪

3.3.3 do_select 核心逻辑

// fs/select.c
int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
{// 初始化等待队列poll_initwait(&table);// 遍历所有文件描述符for (i = 0; i < n; i++) {// 获取文件描述符的poll方法const struct file_operations *f_op = file->f_op;if (f_op && f_op->poll) {// 调用驱动的poll方法,检查事件并注册回调wait_key_set(wait, in, out, exc);mask = (*f_op->poll)(file, wait);}}// 如果没有就绪事件,进入睡眠if (!retval) {retval = poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,to, slack);}// 清理并返回就绪描述符数量poll_freewait(&table);return retval;
}

3.4  优缺点

  • 优点
    • 跨平台支持(几乎所有操作系统都实现了 select)
    • 实现简单,易于理解
  • 缺点
    • FD 数量限制(通常 1024)
    • 每次调用需复制整个 FD 集合,开销大
    • 遍历检查就绪状态的时间复杂度为 O (n),性能随 FD 数量增长而下降

4 poll

4.1 基本概念

poll 的机制与 select 类似,本质上没有太大差别,也是用于管理多个描述符并进行轮询,根据描述符的状态进行处理。它使用 pollfd 结构来描述文件描述符集合,相比 select,poll 没有最大文件描述符数量的限制。

4.2 实现机制

  • 数据结构:使用链表代替固定大小的位图,每个节点包含 FD、关注事件和就绪事件。
  • 调用流程
    1. 用户程序创建 pollfd 数组并填充 FD 和关注事件
    2. 调用 poll() 将 pollfd 数组从用户空间复制到内核空间
    3. 内核遍历所有 pollfd 检查就绪状态
    4. 将就绪事件写入 revents 字段并复制回用户空间
    5. 用户程序遍历 pollfd 数组检查就绪事件
  • 改进点:取消了 FD 数量限制,采用链表存储 FD 可动态扩展。
  • 未解决的问题:仍需复制整个 pollfd 数组,遍历检查时间复杂度仍为 O (n)。

4.3 poll源码分析

4.3.1 关键数据结构

// include/uapi/linux/poll.h
struct pollfd {int fd;         // 文件描述符short events;   // 关注的事件short revents;  // 就绪的事件
};

4.3.2 核心函数调用链

sys_poll()                // 用户调用的系统调用-> do_sys_poll()        // 核心处理函数-> poll_initwait()    // 初始化等待队列-> do_poll()          // 执行poll逻辑-> f_op->poll()     // 调用每个文件描述符的poll方法-> __pollwait()     // 将当前进程添加到等待队列

4.3.3  do_poll 核心逻辑

// fs/select.c
static int do_poll(unsigned int nfds,  struct poll_list *list,struct timespec64 *end_time)
{// 遍历所有pollfdfor (;;) {struct poll_list *walk;int fdcount = 0;// 遍历每个pollfdfor (walk = list; walk != NULL; walk = walk->next) {struct pollfd * pfd, * pfd_end;pfd = walk->entries;pfd_end = pfd + walk->len;for (; pfd != pfd_end; pfd++) {// 获取文件描述符的poll方法if (file->f_op && file->f_op->poll) {// 调用驱动的poll方法mask = file->f_op->poll(file, wait);}// 检查事件是否匹配if ((mask & pfd->events) &&!test_and_set_bit(pfd->fd, bitmap))fdcount++;}}// 如果有就绪事件或超时,返回if (fdcount || timed_out || signal_pending(current))break;// 否则继续睡眠fdcount = poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,to, slack);}return fdcount;
}

4.4  优缺点

  • 优点
    • 取消了 FD 数量限制
    • 采用动态链表存储 FD,可扩展性更好
  • 缺点
    • 仍需复制整个 pollfd 数组
    • 遍历检查时间复杂度仍为 O (n),不适合大量连接场景

5 epoll

5.1 基本概念

epoll 是 Linux 内核为处理大批量文件描述符而改进的 poll,是 select/poll 的增强版本。它基于事件驱动,使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中。

5.2 实现机制

  • 核心数据结构
    • 红黑树:存储注册的 FD,支持 O (log n) 的插入、删除和查找操作
    • 就绪链表:存储就绪的 FD,通过事件回调机制直接加入
  • 三个核心系统调用
    1. epoll_create:创建一个新的 epoll 实例,并返回一个文件描述符 epfd,用于在 epoll 实例上执行后续操作。
    2. epoll_ctl:用于向 epoll 实例中添加、修改或删除文件描述符,需要 epfd、操作类型(如 EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL)、要操作的文件描述符 fd 和指定感兴趣的事件类型 event 这几个参数
    3. epoll_wait:等待 epoll 实例中的 I/O 事件到达,它监视由 epoll_ctl 添加的文件描述符上的事件,需要 epfd 和用于存储就绪事件的数组,以及可选的超时时间。
  • 关键优化
    • 使用事件回调机制,无需遍历所有 FD
    • 通过内存映射技术减少用户 / 内核空间的数据复制
    • 高效的 FD 管理,红黑树支持大规模连接

epoll 内核实现原理中,有红黑树用于记录用户程序注册的 epoll 事件,等待队列用于唤醒 epoll 线程,就绪队列用于记录 socket 读事件和写事件。其流程如下:

  1. 用户程序调用 epoll_create 函数后,在内核创建 struct eventpoll 对象,同时返回一个文件描述符给用户。
  2. 用户程序通过 epoll_ctl 函数添加、修改、删除 socket 事件,注册成功的 socket 事件会插入红黑树。
  3. 如果 epoll 就绪队列有就绪事件,用户程序调用 epoll_wait 函数会成功获取到就绪事件;如果没有就绪事件,则 epoll 线程陷入休眠。
  4. 当 socket 接收到数据后,通过 socket 等待队列可以唤醒休眠的 epoll 线程,并将 socket 封装成 epoll 就绪事件插入就绪队列,epoll 线程将就绪事件拷贝至用户程序。

5.3 epoll源码分析

5.3.1 关键数据结构

// include/linux/epoll.h
struct eventpoll {spinlock_t lock;                // 自旋锁struct mutex mtx;               // 互斥锁wait_queue_head_t wq;           // 等待队列头wait_queue_head_t poll_wait;    // poll方法的等待队列struct list_head rdllist;       // 就绪描述符链表struct rb_root rbr;             // 红黑树根节点struct epitem *ovflist;         // 溢出链表
};struct epitem {struct rb_node rbn;             // 红黑树节点struct list_head rdllink;       // 就绪链表节点struct epoll_filefd ffd;        // 文件描述符信息struct eventpoll *ep;           // 所属的eventpoll对象struct list_head fllink;        // 文件的epitem链表struct epoll_event event;       // 用户关注的事件
};

5.3.2 核心函数调用链

// epoll_create 相关
sys_epoll_create()-> epoll_create1()-> epi = kmem_cache_alloc(epi_cache, GFP_KERNEL);  // 创建epitem-> init_poll_funcptr(&epi->pt, ep_ptable_queue_proc);  // 初始化poll函数指针// epoll_ctl 相关
sys_epoll_ctl()-> ep_insert()      // 添加文件描述符-> ep_modify()     // 修改事件-> ep_delete()     // 删除文件描述符// epoll_wait 相关
sys_epoll_wait()-> ep_poll()-> ep_events_available()  // 检查是否有就绪事件-> fetch_events()         // 将就绪事件从rdllist复制到用户空间-> schedule_hrtimeout_range()  // 没有事件时进入睡眠

5.3.3  ep_poll 核心逻辑

// epoll_create 相关
sys_epoll_create()-> epoll_create1()-> epi = kmem_cache_alloc(epi_cache, GFP_KERNEL);  // 创建epitem-> init_poll_funcptr(&epi->pt, ep_ptable_queue_proc);  // 初始化poll函数指针// epoll_ctl 相关
sys_epoll_ctl()-> ep_insert()      // 添加文件描述符-> ep_modify()     // 修改事件-> ep_delete()     // 删除文件描述符// epoll_wait 相关
sys_epoll_wait()-> ep_poll()-> ep_events_available()  // 检查是否有就绪事件-> fetch_events()         // 将就绪事件从rdllist复制到用户空间-> schedule_hrtimeout_range()  // 没有事件时进入睡眠

5.4  优缺点

  • 优点
    • 无 FD 数量限制,可轻松处理数万甚至数十万个连接
    • 事件驱动机制,时间复杂度 O (1),性能不会随 FD 数量增长而下降
    • 使用内存映射技术减少数据复制,效率高
    • 支持边缘触发(ET)模式,减少不必要的系统调用
  • 缺点
    • 仅 Linux 系统支持,不跨平台
    • 编程模型相对复杂,边缘触发模式需要更严谨的编程逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915984.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915984.shtml
英文地址,请注明出处:http://en.pswp.cn/news/915984.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网易大模型算法面经总结第一篇

网友一 MHA的原理&#xff0c;是如何进行加速的&#xff0c;用的什么框架推理。 回答&#xff1a; ①先答一下什么是MHA&#xff1a;Multi-Head Attention&#xff08;MHA&#xff09;是 Transformer 的核心机制&#xff0c;并行地关注输入序列中不同位置的多种信息 ②回答MHA的…

Vue3 面试题及详细答案120道(91-105 )

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

SAP-MM-物料进销存表

ABAP库存进销存报表程序摘要 该ABAP程序是一个完整的库存进销存报表系统,主要功能包括: 报表类型选择: 物料库存进销存 批次库存进销存 寄售库存进销存 供应商库存进销存 原料库存进销存 主要功能: 从历史数据表(MARDH, MSKAH, MSLBH, MCHBH等)获取期初库存 处理物料移动数…

这几天都是发癫写的

#include <iostream> #include <vector> #include <unordered_map> #include <algorithm> #include <cmath> // for sqrt// Gen-Sort 实现&#xff08;保持不变&#xff09; void genSort(std::vector<int>& arr) {if (arr.empty()) r…

QT6 源,七章对话框与多窗体(11) 进度对话框 QProgressDialog:属性,公共成员函数,槽函数,信号函数,与源代码带注释

&#xff08;1&#xff09; 本类的继承关系 &#xff1a;可见&#xff0c;进度对话框&#xff0c;也是 QDialog 的子类&#xff0c;在其上面又摆放了一些控件&#xff0c;构成了不同用途的对话框。咱们也可以自定义对话框。只是没有 QT 官方大师们做的好。 人家在定义这 6 个子…

学习游戏制作记录(技能系统)7.24

1.技能系统概念首先让我们了解一下游戏的技能本质是什么&#xff0c;以投掷剑为例子&#xff0c;当玩家使用这个技能时&#xff0c;首先会播放玩家的动画&#xff0c;随后通过技能脚本创建一个剑的对象&#xff0c;当剑回收时会再次调用脚本&#xff0c;让它朝向玩家飞来并销毁…

外部存档(External Archive)机制

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…

MybatisPlus操作方法详细总结

摘要&#xff1a;本文围绕 MyBatis-Plus 数据操作展开&#xff0c;涵盖标准数据层 CRUD 与分页查询&#xff1b;以及各种的复杂 SQL 查询&#xff1b;映射匹配&#xff08;TableField、TableName 注解&#xff09;与 ID 生成策略&#xff08;TableId 五种类型及全局配置&#x…

【C语言进阶】动态内存管理的面试题||练习

本节内容专门整理了一些动态内存管理的面试题&#xff0c;配有详细的解答。 目录 1. 看代码说结果 2. 看代码说结果 3. 看代码说结果 4.小乐乐与欧几里得 描述 分析1&#xff1a; 分析2&#xff1a; 代码&#xff1a; 5. 空心正方形 分析&#xff1a; 1. 看代码说结…

【图论】倍增与lca

void dfs(long u,long father){ dep[u]dep[father]1;//只在这里初始化depfor(long i1;(1<<i)<dep[u];i)fa[u][i]fa[fa[u][i-1]][i-1];//只这里用的倍增for(long ihead[u];~i;iedge[i].next){long vedge[i].to;if(vfather)continue;fa[v][0]u;dfs(v,u); }} long lca(lo…

VS Code 美化插件

目录1. Better Comments 更好的注释2. indent-rainbow 彩虹的缩进3. Trailing Spaces 尾随的空格4. Gruvbox Material 护眼的材质5. Md Editor 博客编辑器6. 待补充推荐笔记&#xff1a;VS Code写代码必备的五款代码美化插件 1. Better Comments 更好的注释 Better Comments Be…

火语言 RPA 在日常运维中的实践

在系统运维和技术支持工作中&#xff0c;总有一些操作像 “固定程序” 一样循环往复&#xff1a;定期检查服务器状态、批量处理用户权限申请、手动清理系统日志…… 这些工作步骤固定、逻辑简单&#xff0c;却占用了大量本可用于故障排查和系统优化的时间。近期在优化运维团队的…

FOUPK3system5XOS系统 NTX V2.0发布通知

FOUPK3system5XOS系统NTX V2.0发布通知更新1.系统安全&#xff1a;使用FOUPK3system5XOS NOS X9新内核与FOUPK3system5XOS系统19.63正式版一样提供更好的安全性2.原生应用&#xff1a;启用FOUPK3system5XOS ONS X9 API 72服务FOUPK3system5XOS系统 NTX V2.0用户支持使用FOUPK3…

爬虫算法原理解析

文章目录 核心算法原理 1. 图遍历算法 广度优先搜索(BFS) 深度优先搜索(DFS) 2. URL调度算法 优先级队列调度 3. 页面去重算法 基于哈希的去重 基于布隆过滤器的去重 4. 链接提取与规范化 5. 抓取频率控制算法 6. 增量爬取算法 高级算法策略 1. PageRank算法在爬虫中的应用 2. …

探索双链表:C语言中的链式结构魔法

目录 引言 一、双链表基础 1.1、什么是双链表&#xff1f; 1.2、双链表节点的结构定义 二、双链表的基本操作 2.1、双链表的初始化 2.2、尾插法 2.3、头插 2.4、判断双链表是否为空 2.5、尾删法 2.6、头删法 2.7、查找 2.8、双链表在指定位置之前插入 2.9、双链表…

HTML5 + CSS3模拟西门庆、武大郎和潘金莲的精彩520微信聊天,看完我又相信爱情了

今天520了&#xff0c;我用HTML5 CSS3模拟了西门庆、武大郎和潘金莲的精彩微信聊天&#xff0c;希望你看完以后可以在紧张的工作中&#xff0c;放松一下&#xff0c;开心一下&#xff0c;同时祝你在这个520可以过得开心快乐。 目录 1 实现思路 1.1 聊天实现素材 1.2 HTML布…

【Linux】Linux了解与基本指令(1)

hello~ 很高兴见到大家! 这次带来的是C中关于Linux基本指令这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢? 个 人 主 页: 默|笙 文章目录一、认识Linux二、操作系统&#xff08;OS&#xff09;三、基本指令1. 目录与普通文件1.1 目录1.2 普通文件2. pwd 与…

dify 学习笔记

目录 启动项目 浏览器访问&#xff1a; dify删除工作流 代码是开源dify 启动项目 cd E:\project\qwen\dify-main\docker docker compose up -d 浏览器访问&#xff1a; http://127.0.0.1/apps dify删除工作流 右下角&#xff0c;三个点&#xff0c;点击弹出框&#xff0…

【YOLOv8改进 - 特征融合】FCM:特征互补映射模块 ,通过融合丰富语义信息与精确空间位置信息,增强深度网络中小目标特征匹配能力

YOLOv8目标检测创新改进与实战案例专栏 专栏目录: YOLOv8有效改进系列及项目实战目录 包含卷积,主干 注意力,检测头等创新机制 以及 各种目标检测分割项目实战案例 专栏链接: YOLOv8基础解析+创新改进+实战案例 文章目录 YOLOv8目标检测创新改进与实战案例专栏 介绍 摘要 文…

算法训练营day30 贪心算法④ 重叠问题 452. 用最少数量的箭引爆气球、435. 无重叠区间 、 763.划分字母区间

贪心算法的第四篇博客&#xff0c;主要是重叠问题的练习&#xff0c;思路都较为简单&#xff0c;最后一题可能需要着重思考一下 452. 用最少数量的箭引爆气球 遍历数组&#xff0c;如果存在重叠则减少一支箭&#xff08;不重叠则增加一支箭&#xff09; 重叠的判定&#xff1a…