C++中的多路转接技术之epoll

news2024/9/22 3:44:49

      • `epoll` 是干什么的?
        • 举个简单的例子
      • epoll的相关系统调用
        • **epoll_create**和epoll_create1
          • 区别
        • epoll_ctl
          • 参数解释
        • **epoll_wait**
          • 参数说明
          • 返回值
        • epoll的使用
      • **epoll**工作原理
      • epoll的优点(和 **select** 的缺点对应)
      • epoll工作方式
          • **水平触发**Level Triggered 工作模式
          • 边缘触发Edge Triggered工作模式
        • **对比**LT和ET

epoll 是 Linux 内核提供的一种高效的 I/O 事件通知机制,常用于网络编程中以替代传统的 selectpoll 系统调用。相比于 selectpollepoll 在处理大量并发连接时具有更高的性能和更好的扩展性。

按照man手册的说法: 是为处理大批量句柄而作了改进的poll.

它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44)

它几乎具备了之前poll的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法

epoll 是干什么的?

  1. 监控多个连接epoll 能帮你同时监控很多网络连接,就像一个超级接线员能同时管理很多电话线路一样。它能告诉你哪些连接上有新消息需要处理,哪些连接被挂断了,等等。
  2. 高效通知:传统的 selectpoll 像是老式的接线员,每次都要检查所有的电话线路才能告诉你哪些线路有事。而 epoll 就聪明多了,它只会告诉你那些有变化的线路,大大提高了效率。
  3. 处理大量连接:如果你有上千个网络连接,epoll 能轻松应对。它的效率不会随着连接数量的增加而明显降低,因为它只关注那些真正有事情发生的连接。
举个简单的例子

假设你在经营一个餐厅,你需要管理很多外卖订单,epoll 就像是一个超级助理,帮你监控所有的订单系统,让你知道什么时候有新订单,什么时候订单完成,什么时候客户取消订单。它只会告诉你有变化的订单,而不是每次都汇报所有的订单状态,这样你就可以专注于处理重要的事情,而不用被不必要的信息干扰。

epoll的相关系统调用

epoll 有3个相关的系统调用.

epoll_create和epoll_create1
int epoll_create(int size);

创建一个epoll的句柄.

自从linux2.6.8之后,size参数是被忽略的.

用完之后, 必须调用close()关闭.

int epoll_create1(int flags);
  • flags:可以是以下值之一:
    • 0:不设置任何标志。
    • EPOLL_CLOEXEC:在返回的文件描述符上设置 FD_CLOEXEC 标志,这意味着当调用 exec 函数时,这个文件描述符会自动关闭。

返回值:成功时返回一个 epoll 实例的文件描述符,失败时返回 -1 并设置 errno。

区别
  1. 参数含义
  • epoll_create 需要一个整数参数 size,但这个参数在现代 Linux 内核中已经被忽略了。
  • epoll_create1 需要一个标志参数 flags,可以设置为 0EPOLL_CLOEXEC
  1. 功能
  • epoll_create 是老版本的接口,参数 size 已经没有实际意义。
  • epoll_create1 是新版本的接口,引入了 flags 参数,增加了对 EPOLL_CLOEXEC 标志的支持,使文件描述符更易于管理。

一般建议使用 epoll_create1,因为它是更现代的接口,并且提供了更好的功能和灵活性。

epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数解释
  • epfd:由 epoll_createepoll_create1 创建的 epoll 实例的文件描述符。

  • op:指定操作类型,可以是以下三个值之一:

    • EPOLL_CTL_ADD:注册新的文件描述符到 epoll 实例中。
    • EPOLL_CTL_MOD:修改已经注册的文件描述符的监听事件。
    • EPOLL_CTL_DEL:从 epoll 实例中删除文件描述符。
  • fd:需要管理的目标文件描述符。

  • event:指向一个 epoll_event 结构体的指针,包含需要监听的事件类型及用户数据。

epoll_event 结构体

struct epoll_event {
    uint32_t events;  // 监听的事件类型
    epoll_data_t data;  // 用户数据
};

typedef union epoll_data {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

events可以是以下几个宏的集合:

EPOLLIN:表示对应的文件描述符可以进行读取操作,或者对端正常关闭了(例如,对于一个 socket,这意味着有数据可以读取,或者对端关闭了连接)。

EPOLLOUT:表示对应的文件描述符可以进行写操作(例如,对于一个 socket,这意味着可以写入数据)。

EPOLLPRI:表示对应的文件描述符有紧急的数据可读,这通常指带外数据(Out-Of-Band Data)。带外数据通常用于 TCP 的紧急数据机制。

EPOLLERR:表示对应的文件描述符发生了错误,例如,读写操作失败或者遇到了网络错误。

EPOLLHUP:表示对应的文件描述符被挂断。对于一个 socket,这通常意味着对端关闭了连接,并且不会再有数据到来。

EPOLLET:将 epoll 设置为边缘触发模式(Edge Triggered)。边缘触发模式只会在状态变化时通知一次,即只有在状态发生变化时才会报告事件。与水平触发模式(Level Triggered)相比,边缘触发模式可能需要更频繁地检查文件描述符的状态,因为它不会在状态保持不变的情况下重复报告事件。(下方会详细说)

EPOLLONESHOT:表示只监听一次事件。设置了这个标志的文件描述符在事件触发后会从 epoll 实例中移除,直到你手动将它重新添加到 epoll 实例中。这是为了处理事件后重新注册,以防止在事件处理过程中丢失事件。适用于需要在每次事件发生后都重新注册的场景,确保事件处理的健壮性。

epoll_data是一个联合体,在某些情况下,你可能只需要其中一种东西,当然我们可以看到其中有一个指针参数,这更是加大了灵活性,比如

struct connection_info {
    int fd;
    // 其他与连接相关的数据
};
struct connection_info *conn_info = new connection_info();
conn_info->fd = conn_fd;
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLRDHUP;
ev.data.ptr = conn_info;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev) == -1) {
    perror("epoll_ctl: conn_fd");
    close(conn_fd);
    delete conn_info;
}
//在处理事件时,你可以通过 events[n].data.ptr 访问该指针,并获取结构体中的信息:
struct connection_info *conn_info = static_cast<connection_info*>(events[n].data.ptr);
int fd = conn_info->fd;
// 处理连接的读写等事件

​ 这样的处理可以增强灵活性、空间节省、便于传递数据。

epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
参数说明
  1. epfdepoll 实例的文件描述符,是之前调用 epoll_createepoll_create1 函数时获得的。
  2. events:一个指向 epoll_event 结构体数组的指针,用于存储返回的事件列表。epoll_wait 将填充这个数组,数组的大小由 maxevents 参数指定。
  3. maxeventsevents 数组的大小,即最大可以返回的事件数量。epoll_wait 可能返回的事件数量最多为 maxevents,实际返回的数量由事件发生的数量决定。
  4. timeout:等待事件的超时时间,以毫秒为单位。如果设置为 -1epoll_wait 将会阻塞,直到至少一个事件发生。如果设置为 0epoll_wait 会立即返回,适用于非阻塞检查。如果设置为正数,epoll_wait 会等待指定的时间后返回,适用于有超时要求的场景。
返回值
  • 成功时,返回发生的事件数量。这个数量可能小于或等于 maxevents
  • 如果没有事件发生并且 timeout0,返回 0
  • 失败时,返回 -1 并设置 errno

其中events是一个输出型参数,epoll_wait 会在返回时填充 events 数组,数组的前 nfds 个元素会包含发生的事件。nfdsepoll_wait 返回的事件数量,表明数组中有多少个有效的事件。

处理事件:遍历 events 数组,根据 events[n].events 的值来识别事件类型,并执行相应的处理逻辑。events[n].data 包含与事件相关的文件描述符或其他数据。

epoll的使用

总结一下, epoll的使用过程就是三部曲:

调用epoll_create创建一个epoll句柄;

调用epoll_ctl, 将要监控的文件描述符进行注册;

调用epoll_wait, 等待文件描述符就绪;

epoll工作原理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关.

struct eventpoll{ 
 .... 
 /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/ 
 struct rb_root rbr; 
 /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/ 
 struct list_head rdlist; 
 .... 
};
  1. 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件.
  2. 这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度).
  3. 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法.
  4. 这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中.
  5. 在epoll中,对于每一个事件,都会建立一个epitem结构体.
struct epitem{ 
 struct rb_node rbn;//红黑树节点 
 struct list_head rdllink;//双向链表节点 
 struct epoll_filefd ffd; //事件句柄信息 
 struct eventpoll *ep; //指向其所属的eventpoll对象 
 struct epoll_event event; //期待发生的事件类型 
}

当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可.

如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度是O(1).

epoll的优点(和 select 的缺点对应)

  • 接口使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效. 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开
  • 数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)
  • 事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符结构加入到就绪队列中, epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度O(1). 即使文件描述符数目很多, 效率也不会受到影响.
  • 没有数量限制: 文件描述符数目无上限

epoll工作方式

你正在吃鸡, 眼看进入了决赛圈, 你妈饭做好了, 喊你吃饭的时候有两种方式:

  1. 如果你妈喊你一次, 你没动, 那么你妈会继续喊你第二次, 第三次…(亲妈, 水平触发)
  2. 如果你妈喊你一次, 你没动, 你妈就不管你了(后妈, 边缘触发)

epoll有2种工作方式-水平触发(LT)和边缘触发(ET)

假如有这样一个例子:

我们已经把一个tcp socket添加到epoll描述符

这个时候socket的另一端被写入了2KB的数据

调用epoll_wait,并且它会返回. 说明它已经准备好读取操作

然后调用read, 只读取了1KB的数据

继续调用epoll_wait…

水平触发Level Triggered 工作模式

epoll默认状态下就是LT工作模式.

当epoll检测到socket上事件就绪的时候, 可以不立刻进行处理. 或者只处理一部分.

如上面的例子, 由于只读了1K数据, 缓冲区中还剩1K数据, 在第二次调用 epoll_wait 时, epoll_wait

仍然会立刻返回并通知socket读事件就绪.

直到缓冲区上所有的数据都被处理完, epoll_wait 才不会立刻返回.

支持阻塞读写和非阻塞读写

边缘触发Edge Triggered工作模式

如果我们在第1步将socket添加到epoll描述符的时候使用了EPOLLET标志, epoll进入ET工作模式.

  • 当epoll检测到socket上事件就绪时, 必须立刻处理.
  • 如上面的例子, 虽然只读了1K的数据, 缓冲区还剩1K的数据, 在第二次调用 epoll_wait 的时候, epoll_wait 不会再返回了.
  • 也就是说, ET模式下, 文件描述符上的事件就绪后, 只有一次处理机会.
  • ET的性能比LT性能更高( epoll_wait 返回的次数少了很多). Nginx默认采用ET模式使用epoll.
  • 只支持非阻塞的读写

select和poll其实也是工作在LT模式下. epoll既可以支持LT, 也可以支持ET.

对比LT和ET

LT是 epoll 的默认行为. 使用 ET 能够减少 epoll 触发的次数. 但是代价就是强逼着程序猿一次响应就绪过程中就把所有的数据都处理完.

相当于一个文件描述符就绪之后, 不会反复被提示就绪, 看起来就比 LT 更高效一些. 但是在 LT 情况下如果也能做到每次就绪的文件描述符都立刻处理, 不让这个就绪被重复提示的话, 其实性能也是一样的.

另一方面, ET 的代码复杂程度更高了.

select和poll其实也是工作在LT模式下. epoll既可以支持LT, 也可以支持ET

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

针对汽车应用而设计的SCT4026D、SCT4062K、SCT3105K、SCT3080A、SCT3060A全新系列碳化硅 (SiC) MOSFET

全新系列碳化硅 (SiC) MOSFET SCT4026DWAHRTL SCT4062KWAHRTL SCT3105KRC15 SCT3080ALHRC11 SCT3080ARC15 SCT3060ARC15 ——明佳达 AEC-Q101 SiC功率MOSFETs是汽车和开关电源的理想选择。SiC功率MOSFETs可以提高开关频率&#xff0c;减少所需的电容、电抗器和其他元件的体积…

react开发-配置开发时候@指向SRC目录

这里写目录标题 配置开发时候指向SRC目录VScode编辑器给出提示总体1.配置react的 2.配置Vscode的1.配置react的2,配置VSCode的提示支持 配置开发时候指向SRC目录VScode编辑器给出提示 总体1.配置react的 2.配置Vscode的 1.配置react的 1. 我么需要下载一个webpack的插件 这样…

【闲谈】我的创作纪念日(CrowdStrike、无人驾驶)

感谢地心引力 &#xff0c;有幸再次遇见你&#xff1a; 还记得 2020 年 07 月 22 日吗&#xff1f;你撰写了第 1 篇技术博客&#xff1a;《遗传算法实例解析》在这平凡的一天&#xff0c;你赋予了它不平凡的意义。也许是立志成为一名专业 IT 作者、也许是记录一段刚实践的经验。…

【iOS】——探究isKindOfClass和isMemberOfClass底层实现

isKindOfClass 判断该对象是否为传入的类或其子类的实例 // 类方法实现&#xff0c;用于检查一个类是否属于另一个类或其父类链上的任何类。(BOOL)isKindOfClass:(Class)cls {// 从当前类开始&#xff0c;tcls将沿着元类的继承链向上遍历。for (Class tcls self->ISA(); …

MySQL:库表操作

MySQL&#xff1a;库表操作 库操作查看创建字符编码集 删除修改备份 表操作创建查看删除修改 库操作 查看 查看存在哪些数据库&#xff1a; show databases;示例&#xff1a; 查看自己当前处于哪一个数据库&#xff1a; select database();示例&#xff1a; 此处由于我不处于任…

Unity UGUI 之 Input Field

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 1.Input Field是什么&#xff1f; 给玩家提供输入的输入框 2.重要参数 中英文对照着看…

JSONNode树形解析或流式解析

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 什么是JSONNode&#xff1f; JSONNode是一个用于处理JSON数据的数据结构&#xff0c;它提供了一种简单、灵活、高效的方式来操作JSON数据。JSONNode可以看作是一个树形结构&#xff0c;其中每个节点都可以包含一个值…

MongoDB自学笔记(四)

一、前文回顾 上一篇文章中我们学习了MongoDB中的更新方法&#xff0c;也学了一部分操作符。今天我们将学习最后一个操作“删除”。 二、删除 原始数据如下&#xff1a; 1、deleteOne 语法&#xff1a;db.collection.deleteOne(< query >,< options >) 具体参…

OpenCV 像素操作—证件照换底色详细原理 C++纯手写实现

文章目录 总体步骤1.RGB转HSV2.找出要换的底色3.取反&#xff0c;黑白颠倒4.将原图像的非背景部分复制到新背景上 完整代码1.C纯手写版2.官方API版本 总体步骤 1.RGB转HSV 为什么一定要转为HSV 颜色空间&#xff1f; 将图像从BGR颜色空间转换为HSV颜色空间是因为HSV颜色空间更…

vscode 文件颜色变绿色

解决&#xff1a;关闭git功能 在设置中搜索Git:Enabled&#xff0c;取消Decorations: Enabled的勾选

内网渗透隧道构建,使用github项目联动msf绕uac,使用简单的spp来进行操作icmp隧道

在我们需要木马上线的时候&#xff0c;发现上线不了&#xff0c;我们一般就想到建立隧道&#xff0c;来解决问题&#xff0c;或者是说我们直接还一种连接的操作来进行上线。比如说我们正向连接上不了&#xff0c;我们可以还成反向连接的操作。或者我们使用隧道直接硬刚waf来进行…

计算机毕业设计-程序论文文档-基于SSM的驾校管理系统

本系统开发采用技术为JSP、Bootstrap、Ajax、SSM、Java、Tomcat、Maven 此文章为本人亲自指导加编写&#xff0c;禁止任何人抄袭以及各类盈利性传播&#xff0c; 相关的代码部署论文ppt代码讲解答辩指导文件都有可私要 项目源码&#xff0c;请关注❥点赞收藏并私信博主&#x…

代码随想录 day 18 二叉树

第六章 二叉树part06 详细布置 530.二叉搜索树的最小绝对差 需要领悟一下二叉树遍历上双指针操作&#xff0c;优先掌握递归 题目链接/文章讲解&#xff1a;https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%B…

鸿蒙OpenHarmony Native API【HiLog】

HiLog Overview Description: HiLog模块实现日志打印功能。 开发者可以通过使用这些接口实现日志相关功能&#xff0c;输出日志时可以指定日志类型、所属业务领域、日志TAG标识、日志级别等。 syscap SystemCapability.HiviewDFX.HiLog Since: 8 Summary Files File …

甄选范文“论企业集成平台的理解与应用”,软考高级论文,系统架构设计师论文

论文真题 企业集成平台(Enterprise Imtcgation Plaform,EIP)是支特企业信息集成的像环境,其主要功能是为企业中的数据、系统和应用等多种对象的协同行提供各种公共服务及运行时的支撑环境。企业集成平台能够根据业务模型的变化快速地进行信息系统的配置和调整,保证不同系统…

HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号3

基础认证题库请移步&#xff1a;HarmonyOS应用开发者基础认证题库 注&#xff1a;有读者反馈&#xff0c;题库的代码块比较多&#xff0c;打开文章时会卡死。所以笔者将题库拆分&#xff0c;单选题20个为一组&#xff0c;多选题10个为一组&#xff0c;题库目录如下&#xff0c;…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 堆内存申请(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

爬虫自己做的

1.urllib 1.1基本使用 1.2 下载&#xff08;图片&#xff0c;页面&#xff0c;视频&#xff09; 1.3 get 1.3.1 quote 中文变成对应uncode编码 当url 的wd中文时 quote是将中文变成对应uncode编码 然后拼接成完整的url 1.3.2urlencode方法 wd有多个参数 1.3.3ajas get实例 …

科普文:银行信贷系统概叙

信贷业务流程 资金需求者提交申请&#xff1a;资金需求者通过不同渠道&#xff08;如APP、网站、门店等&#xff09;提交贷款申请。 系统交互完成审批&#xff1a;系统通过自动化和人工相结合的方式&#xff0c;对贷款申请进行初步筛选和审批。 系统交互完成策略判断&#xf…

java学习---小项目---租房系统

package com.project.House_rental.HouseApp;import com.project.House_rental.HouseView.HouseView; //主界面 public class HouseApp {public static void main(String[] args) {new HouseView().List_();System.out.println("------已退出----------");} }package…