接上文poll机制:Linux 系统 poll 与 epoll 机制1。

3. epoll 机制:高并发 I/O 的优化实现​

epoll(Efficient event polling implementation)机制诞生于 Linux 2.5.44 版本,是内核为解决高并发 I/O 场景(如万级以上 FD 监听)而设计的新一代 I/O 多路复用技术。epoll全称event poll,但其中的e有double e的味道:efficient & event。它通过红黑树管理注册 FD、就绪链表存储就绪 FD、内存映射(mmap)减少拷贝三大优化,彻底解决了poll的性能瓶颈,实现了 “O (1) 就绪事件查询”,成为高性能服务器的标配。​

3.1 epoll 的核心接口与数据结构​

3.1.1 三大核心系统调用​

epoll 通过三个独立的系统调用来实现 “FD 注册 - 事件监听 - 就绪查询” 的完整流程,避免了poll每次调用都需重新传递 FD 集合的问题。epool是anon_inode技术的一个应用案例。

  1. epoll_create():创建一个 epoll 实例(内核的核心数据结构);​

  2. epoll_ctl():向 epoll 实例中添加、修改或删除需监听的 FD 及其事件;​

  3. epoll_wait():等待 epoll 实例中注册的 FD 就绪,并返回就绪事件。​

3.1.2 关键数据结构​

epoll 的内核实现依赖三个核心数据结构:struct eventpoll、struct epitem、struct epoll_event。​

(1)用户空间的struct epoll_event​

用户进程通过该结构体与内核交互,描述 FD 的监听事件和就绪状态,定义如下:

#include <sys/epoll.h>​
struct epoll_event {​uint32_t events;  // 监听事件/就绪事件(与poll的events含义类似)​epoll_data_t data; // 关联的用户数据(如FD、自定义指针)​
};​
​
// 联合体,存储用户数据​
typedef union epoll_data {​void    *ptr;    // 自定义指针(可指向用户空间数据)​int      fd;     // 关联的文件描述符​uint32_t u32;    // 32位无符号整数​uint64_t u64;    // 64位无符号整数​
} epoll_data_t;

与pollfd相比,epoll_event的优势是通过epoll_data_t联合体支持自定义数据,方便用户进程关联 FD 的上下文信息(如客户端连接的会话数据)。​

(2)内核中的struct epitem​

epitem是内核中描述 “epoll 实例与 FD 关联关系” 的结构,每个注册到 epoll 的 FD 对应一个epitem,定义简化如下:

struct epitem {​struct rb_node rbn;          // 红黑树节点(用于加入epoll实例的红黑树)​struct list_head rdllink;    // 就绪链表节点(FD就绪时加入就绪链表)​struct file *file;           // 关联的FD对应的struct file​struct epoll_event event;    // 存储用户设置的监听事件与用户数据​struct epoll_table_struct *epoll_table; // 关联的poll_table​
};
  • rbn:用于将epitem插入 epoll 实例的红黑树,实现 FD 的快速查找、插入、删除;​

  • rdllink:用于 FD 就绪时,将epitem插入 epoll 实例的就绪链表,避免遍历所有 FD;​

  • epoll_table:关联poll_table,用于将进程注册到 FD 的等待队列。​

(3)内核中的struct eventpoll​

eventpoll是 epoll 实例的核心结构,每个epoll_create()调用会创建一个eventpoll,该对象是内核对象通过anon_inode技术供用户态访问。定义简化如下:

struct eventpoll {​spinlock_t lock;             // 保护红黑树和就绪链表的自旋锁​struct rb_root rbr;          // 红黑树的根节点(管理所有注册的epitem)​struct list_head rdllist;    // 就绪链表的头节点(存储所有就绪的epitem)​wait_queue_head_t wq;        // epoll的等待队列(存储调用epoll_wait的进程)​struct page *tmp_page;       // 用于mmap的内存页(减少用户态与内核态拷贝)​
};
  • rbr:红黑树的根,用于管理所有通过epoll_ctl注册的epitem,红黑树的 key 是 FD,确保插入、删除、查找的时间复杂度为 O (logn);​

  • rdllist:就绪链表,存储所有就绪的epitem,epoll_wait只需遍历该链表即可获取就绪 FD,无需遍历所有注册 FD;​

  • wq:epoll 的等待队列,当无 FD 就绪时,调用epoll_wait的进程会睡眠在该队列中,FD 就绪时被唤醒;​

  • tmp_page:通过mmap将内核内存页映射到用户空间,避免epoll_event数组的拷贝。

3.2 epoll 的核心流程(基于 ET 模式)​

epoll 支持两种触发模式:水平触发(Level Trigger,LT) 和边缘触发(Edge Trigger,ET)。LT 模式与poll类似,只要 FD 就绪,每次调用epoll_wait都会返回该 FD;ET 模式仅在 FD 状态从 “未就绪” 变为 “就绪” 时返回一次,需配合非阻塞 I/O 使用,避免漏读数据。以下以 ET 模式为例,拆解 epoll 的核心流程。​

3.2.1 步骤 1:创建 epoll 实例(epoll_create)​

用户进程调用epoll_create(size_t size)(注:size参数在 Linux 2.6.8 后被忽略,仅用于兼容旧版本),内核会:​

  1. 分配一个struct eventpoll结构体;​

  2. 初始化结构体中的红黑树(rbr)、就绪链表(rdllist)、等待队列(wq);​

  3. 分配一个内存页(tmp_page),用于后续mmap;​

  4. 创建一个匿名文件(内核内部使用),并返回一个epoll 文件描述符(epfd),用户进程通过epfd操作该 epoll 实例。​

3.2.2 步骤 2:注册 FD 到 epoll 实例(epoll_ctl)​

用户进程调用epoll_ctl(int epfd, int op, int fd, struct epoll_event *event),其中op为操作类型(EPOLL_CTL_ADD:添加、EPOLL_CTL_MOD:修改、EPOLL_CTL_DEL:删除),内核会:​

  1. 通过epfd找到对应的eventpoll结构体;​

  2. 根据op类型执行对应操作。

OP操作
EPOLL_CTL_ADD(添加 FD)

a. 检查fd是否已注册(通过红黑树查找epitem),若已注册则返回错误;​

b. 分配一个struct epitem结构体,初始化rbn(红黑树节点)、rdllink

(就绪链表节点),并关联fd对应的struct file和用户传入的event;​

c. 将epitem插入eventpoll的红黑树(rbr);​

EPOLL_CTL_MOD(修改 FD)

a. 通过红黑树查找fd对应的epitem;​

b. 更新epitem中的event字段(如修改监听事件);​

c. 重新调用设备poll方法,更新等待队列注册。​

d. 调用fd对应的设备poll方法,传递epoll_table(由epitem关联),

将当前进程注册到fd的等待队列中(若后续fd就绪,会触发唤醒逻辑)。​

EPOLL_CTL_DEL(删除 FD)

a. 通过红黑树查找fd对应的epitem;​

b. 将epitem从红黑树和就绪链表中移除;​

c. 调用设备poll方法,将进程从fd的等待队列中删除;​

d. 释放epitem结构体。​

3.2.3 步骤 3:等待 FD 就绪(epoll_wait)​

用户进程调用epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout),其中events是用户空间存储就绪事件的数组,maxevents是数组长度,内核会:​

  1. 通过epfd找到eventpoll结构体,并加锁(spinlock);​

  2. 检查就绪链表(rdllist)是否为空:​

  • 若非空:遍历就绪链表,将每个epitem的event(含就绪事件和用户数据)拷贝到events数组中,最多拷贝maxevents个元素;​

  • 若为空:判断timeout值,若timeout > 0,则将当前进程睡眠在eventpoll的等待队列(wq)中,直到 FD 就绪、超时或被信号中断;若timeout = 0,直接返回 0;若timeout = -1,无限等待。

       3. ​解锁并返回就绪事件的数量(若超时返回 0,失败返回 - 1)。​

3.2.4 步骤 4:FD 就绪与进程唤醒​

当某个注册的 FD 就绪时(如 TCP socket 收到数据),内核会:​

  1. 触发 FD 所属设备的中断处理函数(如网络中断);​

  2. 中断处理函数调用 FD 的poll方法,判断 FD 状态并标记为就绪;​

  3. 找到该 FD 对应的epitem(通过struct file关联),将epitem加入eventpoll的就绪链表(rdllist);​

  4. 遍历eventpoll的等待队列(wq),唤醒所有睡眠在该队列中的进程(即调用epoll_wait的进程);​

  5. 进程被唤醒后,重新执行epoll_wait的逻辑,遍历就绪链表并返回就绪事件。​

3.2.5 步骤 5:用户进程处理 I/O 事件​

用户进程通过events数组获取就绪 FD 后,需:​

  1. 检查每个就绪 FD 的events字段(如POLLIN),确定事件类型;​

  2. 由于 ET 模式仅通知一次,需通过非阻塞 I/O(如read时设置O_NONBLOCK)循环读取 / 写入数据,直到返回EAGAIN(无更多数据可读 / 写);​

  3. 处理完数据后,可继续调用epoll_wait等待下一次事件。​

3.3 epoll 的效率优化关键点​

epoll 相比poll的性能优势,源于四个核心优化:​

  • 红黑树管理注册 FD:O (logn) 的操作复杂度​

poll每次调用都需遍历所有 FD,时间复杂度为 O (n);而 epoll 通过红黑树管理注册的 FD,插入、删除、查找的时间复杂度均为 O (logn),即使 FD 数量达到 10 万级,操作耗时也可忽略。​

  • 就绪链表存储就绪 FD:O (1) 的就绪查询​

poll需遍历所有 FD 才能判断是否就绪,而 epoll 在 FD 就绪时主动将其加入就绪链表,epoll_wait只需遍历就绪链表即可获取就绪 FD,时间复杂度为 O (k)(k 为就绪 FD 数量),当大多数 FD 未就绪时(高并发场景常见),效率提升极为显著。​

  • mmap 减少用户态与内核态拷贝​

poll每次调用都需拷贝struct pollfd数组(用户态→内核态→用户态),而 epoll 通过mmap将内核中的tmp_page内存页映射到用户空间,epoll_event数组直接在映射内存中传递,无需拷贝,大幅减少了数据传输开销。​

  • 边缘触发(ET)减少无效通知​

LT 模式下,若用户进程未完全处理 FD 数据,内核会反复通知该 FD,导致无效系统调用;ET 模式仅在 FD 状态变化时通知一次,配合非阻塞 I/O 可避免无效通知,减少系统调用次数,进一步提升效率。

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

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

相关文章

Mamba LLM 架构简介:机器学习的新范式

Mamba LLM 架构简介&#xff1a;机器学习的新范式探索 Mamba LLM 的强大功能&#xff0c;Mamba LLM 是来自一流大学的变革性架构&#xff0c;重新定义了 AI 中的序列处理。语言模型是一种经过训练的机器学习模型&#xff0c;用于在自然语言上执行概率分布。它们的架构主要由多层…

GaussDB生产扩容引起的PANIC问题处理案例

1 环境信息CPU:8C内存&#xff1a;64GGaussDB版本&#xff1a;24.7.32解决方案部署形态&#xff1a;HCS部署形态&#xff1a;1主1从1日志扩容原因&#xff1a;当前的配置满足不了max_connections为2000值&#xff0c;即当前的业务最大连接数超过2000个而按照8C64G的配置最多满足…

【168页PPT】华为流程管理体系构建与落地(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92796370/91662548 资料解读&#xff1a;【168页PPT】华为流程管理体系构建与落地 详细资料请看本解读文章的最后内容。华为&#xff0c;作为全球知名…

基于CotSegNet网络和机器学习的棉花点云器官分割和表型信息提取

一、引言PointNet作为点云处理领域的先驱与里程碑式深度学习模型&#xff0c;以其卓越的性能和对无序点云数据直接处理的能力而闻名。博主将分享1篇发表在《Computers and Electronics in Agriculture》&#xff08;中科院1区TOP&#xff09;的“Organ segmentation and phenot…

经典卷积神经网络CNN

一、CNN视觉处理三大任务&#xff1a;图像分类、目标检测、图像分割上游&#xff1a;提取特征&#xff0c;CNN下游&#xff1a;分类、目标、分割等&#xff0c;具体的业务1. 概述卷积神经网络是深度学习在计算机视觉领域的突破性成果。在计算机视觉领域, 往往我们输入的图像都很…

11.1.5 实现文件删除,共享和共享下载排行榜

1、图床分享图片api_sharepicture.cc sharepicture_cgi.c 分享后每个人都可以看到。 数据库&#xff1a; DROP TABLE IF EXISTS share_picture_list; CREATE TABLE share_picture_list (id int(11) NOT NULL AUTO_INCREMENT COMMENT 编号,user varchar(32) NOT NULL COMMENT …

【Java后端】SpringBoot配置多个环境(开发、测试、生产)

在 Spring Boot 中配置多个环境&#xff08;开发、测试、生产&#xff09;通常用 配置文件分环境管理 启动参数切换 的方式来实现。下面一个完整的实践指南&#xff1a;&#x1f539; 1. 使用多配置文件管理环境 Spring Boot 默认支持 application-{profile}.properties 或 ap…

HTTP 分块传输编码:深度解析与报文精髓

分块传输编码&#xff08;Chunked Transfer Encoding&#xff09;是 HTTP/1.1 协议中的一项核心特性&#xff0c;它允许服务器在不预先知道响应体总大小的情况下&#xff0c;高效地传输数据。这项技术解决了传统 Content-Length 机制的局限性&#xff0c;使得 HTTP 协议能够完美…

Vue 项目首屏加载速度优化

Vue 项目首屏加载从 5s 到 1.5s&#xff1a;4 步落地优化方案&#xff0c;附完整代码 数据对比前段时间我在做一个活动时&#xff0c;打包加载后发现打开页面要等半天&#xff0c;经过几天的优化&#xff0c;最终将首屏加载时间从5秒压到 1.5 秒。这篇文章会把整个优化过程拆解…

Java学习第十六部分——JUnit框架

目录 一.概述 二.作用 三.版本 四.优势 五.局限性 六.发展方向 七.核心组件 1 测试用例 2.断言&#xff08;Assertions&#xff09; 3.测试生命周期 4.测试运行器 八.简单示例 九.JUnit 4 与 JUnit 5 的区别 十.idea项目实战 1.在idea中创建Java项目&#xff0c…

[吾爱原创] 千千每日计划

[吾爱原创] 千千每日计划 链接&#xff1a;https://pan.xunlei.com/s/VOYuE8p-KIV-NJr2_0d1Ak9YA1?pwdbqez# 介绍&#xff1a;千千系列的最后一款软件,一款每日计划的一款软件&#xff0c;并且支持时间段修改和打卡和导入导出等功能。 功能&#xff1a; 1.设置每天的计划 2…

docker命令(二)

目录 docker命令 1.inspect命令&#xff08;查看镜像信息&#xff09; 2.tag命令&#xff08;为镜像起别名&#xff09; 3.--help命令&#xff08;查看命令的使用帮组&#xff09; docker 命令 --help docker --help 4.run命令 1.格式 2.启动tomcat镜像 3. docker 不能被外部访…

Dockerfile实现java容器构建及项目重启(公网和内网)

公网情况0.Dockerfile关键字关键字作用一句话出现位置FROM指定基础镜像&#xff08;任何 Dockerfile 必须且首行&#xff09;全局RUN在镜像构建阶段执行命令&#xff08;常用来安装软件&#xff09;构建期COPY把宿主机文件/目录复制进镜像构建期ADD类似 COPY&#xff0c;但额外…

SpringCloud与Dubbo深度对比:架构、性能与生态全解析

引言在微服务架构盛行的今天&#xff0c;服务治理框架的选择成为企业技术栈决策的关键环节。Spring Cloud和Dubbo作为Java生态中最具代表性的两大微服务框架&#xff0c;各自拥有独特的优势和适用场景。本文将从架构设计、服务治理、性能表现、生态系统等多个维度进行深度对比&…

简历书写---自我评价怎么写

前言 今天一对一辅导了很多同学做简历&#xff0c;看到很多同学简历上都有一栏&#xff1a;自我评价 那我们就要思考一下&#xff0c;我们搞技术的&#xff0c;一份技术简历&#xff0c;自我评价上怎么写&#xff0c;才能算一个加分点呢&#xff1f; 观点分享 首先&#xff0c;…

嵌入式Linux学习 - 数据库开发

目录 一. 在终端的使用 1. 下载 2. 操作 3. 相关函数 1.增 2. 删 3. 改 4. 查 5. 补充函数 二. 在软件的使用 1. 下载 2. 操作 三. 在编程的使用 1. 下载 2. 相关函数 1. 打开 2. 读写执行sql语句 3. 关闭 一. 在终端的使用 1. 下载 sudo apt-get install …

产品运营必备的职场通用能力有哪些?如何一步步提升?

在流量红利消退的存量竞争时代&#xff0c;产品运营岗位正经历价值重构。单纯的活动策划与用户维护已无法满足发展需求&#xff0c;数据驱动的精细化运营成为行业分水岭。面对这场变革&#xff0c;复合能力建设与前瞻工具掌握是运营人突破天花板的密钥。推荐考取CDA数据分析师&…

ESPTimer vs GPTimer:ESP32 定时器系统深度解析

第十五章和第十六章分别学习了​​ESPTimer​​ 和 ​​GPTimer​​ &#xff0c;那这两种定时器有什么区别&#xff0c;如何使用呢&#xff0c;下面探讨下。1. 两种定时器对比介绍1.1 两种定时器设计在 ESP32 开发中&#xff0c;​​ESPTimer​​ 和 ​​GPTimer​​ 是两种完…

【70页PPT】WMS助力企业数字化转型(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92808811/91806268 资料解读&#xff1a;【70页PPT】WMS助力企业数字化转型 详细资料请看本解读文章的最后内容。仓储管理在企业运营中占据关键地位&a…

[光学原理与应用-337]:ZEMAX - 自带的用于学习的样例设计

ZEMAX&#xff08;OpticStudio&#xff09;内置了大量样例设计文件&#xff0c;这些文件覆盖了从基础光学原理到复杂系统设计的全场景&#xff0c;是学习光学设计、掌握软件操作、理解像差理论的绝佳资源。以下是ZEMAX自带样例设计的详细分类、使用方法及学习价值分析&#xff…