高级I/O知识分享【epoll || Reactor ET,LT模式】

   博客主页:花果山~程序猿-CSDN博客

文章分栏:Linux_花果山~程序猿的博客-CSDN博客

关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长!

在这里插入图片描述

目录

一,接口

epoll_create

 epoll_ctl

event 事件类型:

epoll_wait

二,epoll优点(相较select,poll)

三,epoll有2种工作方式

如何理解两种工作方式:(快递员例子)

水平触发Level Triggered 工作模式

边缘触发Edge Triggered工作模式

epoll使用场景

epoll中的惊群问题(选学)

ET模式使用思路


嗨!收到一张超美的图,愿你每天都能顺心!

一,接口

epoll_create

epoll_create(size_t size)

用于创建一个epoll文件描述符,返回一个非负整数表示新创建的epoll实例的文件描述符。size是一个建议值,表示最初能容纳多少个事件,但实际上内核可能会忽略此参数。

  • 参数
    • size:建议的初始事件槽的数量,但在现代内核版本中此参数几乎无用,内核会根据需要动态调整。

 epoll_ctl

epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

用于向epoll实例添加、修改或删除文件描述符的监听事件。

  • 参数
    • epfdepoll_create返回的epoll文件描述符。
    • op:操作类型,可以是EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除)。
    • fd:需要操作的文件描述符。
    • event:指向struct epoll_event结构体的指针,包含需要监控的事件类型。

event 事件类型:

  1. EPOLLIN - 表示描述符可读(例如,有数据可读取)。
  2. EPOLLOUT - 表示描述符可写(例如,可以发送数据)。
  3. EPOLLERR - 表示描述符有错误。
  4. EPOLLHUP - 表示描述符挂起(例如,对端关闭了连接)。
  5. EPOLLET - 这是一个边缘触发模式标志,不是事件类型,但它可以与其他事件类型结合使用,以改变事件检测的行为。
  6. EPOLLONESHOT - 这个标志让 epoll_wait() 在第一次匹配到这个事件后就不再为这个文件描述符报告该事件,直到 epoll_ctl() 再次修改此文件描述符的监听条件。
  7. EPOLLEXCLUSIVE - 当设置此标志时,如果多个进程或线程尝试等待同一个事件,那么仅有一个等待者会被唤醒

epoll_wait

epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

等待已注册文件描述符上的I/O事件发生,并返回就绪事件的数目。

  • 参数
    • epfdepoll_create返回的epoll文件描述符。
    • events:一个指向epoll_event数组的指针,用于返回就绪事件。
    • maxevents:最大可返回的就绪事件数。
    • timeout:等待的超时时间(毫秒为单位)。如果设置为负数或0,则epoll_wait立即返回;如果大于0,则表示等待的时间。

结构体epoll_event: 

从底层原理理解三接口负责的功能图:

当某一进程调用 epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关
struct eventpoll{ .... /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/ struct rb_root rbr; /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/ struct list_head rdlist; .... 
};

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件,这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度). 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法.

这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中.
在epoll中,对于每一个事件,都会建立一个epitem结构体.
struct epitem{ struct rb_node rbn;//红黑树节点 struct list_head rdllink;//双向链表节点 struct epoll_filefd ffd; //事件句柄信息 struct eventpoll *ep; //指向其所属的eventpoll对象 struct epoll_event event; //期待发生的事件类型 
}
如果 rdlist 不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户 . 这个操作的时间复杂度是O(1)

如何理解参数epfd的作用?

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

  • 调用epoll_create创建一个epoll句柄;
  • 调用epoll_ctl, 将要监控的文件描述符进行注册;
  • 调用epoll_wait, 等待文件描述符就绪;

二,epoll优点(相较select,poll)

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

三,epoll2种工作方式

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

如何理解两种工作方式:(快递员例子)

LT:  当你的外卖(数据)到时,外卖员(底层)会一直给你打电话(通知)直到你下来将你的所有外卖都取走(数据拿走 )。
ET:  外卖来时,外卖员(底层)只给你打一次电话,你如果不下来取,外卖员(底层)不会再通知你,你的外卖(数据)就再也拿不到了。

比较标准的解释:

水平触发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模式下,只有一次处理机会,这样倒逼程序员,要一次取完所有的数据
  • ET的性能LT性能更高( 相同的运行时间内epoll_wait 返回的次数少了很多== 无效通知减少 == 增加其他socket通知的数量). Nginx默认采用ET模式使用epoll.
  • 只支持非阻塞的读写
select poll 其实也是工作在 LT 模式下 . epoll 既可以支持 LT, 也可以支持ET。
LT 情况下如果也能做到每次就绪的文件描述符都立刻处理, 不让这个就绪被重复提示的话 , 其实性能也是一样的 . 但是另一方面, ET 的代码复杂程度更高了。

epoll使用场景

epoll的高性能, 是有一定的特定场景的. 如果场景选择的不适宜, epoll的性能可能适得其反.
  • 对于多连接, 且多连接中只有一部分连接比较活跃时, 比较适合使用epoll.
例如 , 典型的一个需要处理上万个客户端的服务器 , 例如各种互联网 APP的入口服务器 , 这样的服务器就很适合 epoll. 如果 只是系统内部, 服务器和服务器之间进行通信, 只有少数的几个连接, 这种情况下用epoll就并不合适 . 具体要根 据需求和场景特点来决定使用哪种IO 模型。

epoll中的惊群问题(选学)

惊群问题有些面试官可能会问到 . 建议同学们课后自己查阅资料了解一下问题的解决方案。
参考 http://blog.csdn.net/fsmiy/article/details/36873357

ET模式使用思路

1.epoll_ctl时添加的文件描述符,需要添加设置 EPOLLET,这样一旦事件就绪,通过epoll_wait报告一次。
2.将需要设置的fd,如listen_socket,设置为非阻塞式;在通过accept系统调用时进行轮询,直到资源被全部提取后,才结束轮询。(采用非阻塞式,就是为了避免一次未取完资源,ET模式下,事件不再通知,导致事件资源丢失)——>可参考fcntl接口的非阻塞例子
例如:这个监听套接字的例子

选择ET和LT

        选择哪种模式取决于具体的应用场景和需求。如果你的应用程序需要处理大量并发连接,并且对实时性要求较高,那么 ET 模式可能是更好的选择。如果你的应用程序需要处理长时间存在的连接,并且更关注稳定性,那么 LT 模式可能是更好的选择。

用ET模式优化自主web服务器

        Apache 和 Nginx 是两款广泛使用的 Web 服务器软件,它们在处理大量并发连接方面表现出色。为了实现高效的并发处理,这两款服务器都利用了 Linux 内核提供的高性能 I/O 多路复用机制 epoll。 

  • Apache:通常使用 Level Triggered (LT) 模式,因为它提供了更好的稳定性和易用性。
  • Nginx:通常使用 Edge Triggered (ET) 模式,因为它提供了更高的性能和实时性。

框架图

简单实现自主 web服务器的epoll的 ET模式框架图

优化心得总结

读事件思维逻辑:

系统会自动检测已经被标识的套接字,当套接字有数据到达时,触发读事件,并自动将该事件加载到 epoll 的事件队列中,随后用户可以通过不断轮询 epoll_wait 函数来获取并处理这些事件(如处理方法监听套接字上的 accept 操作)。

写事件思维逻辑:

        当一个套接字的发送缓冲区满时,写操作会被阻塞。内核维护了一个发送缓冲区的状态,当缓冲区中有足够的空间可以写入数据时,内核会将该文件描述符标记为可写(EPOLLOUT 事件就绪)。

关于写事件逻辑理解:

1. 刚连接时是否需要设置写事件?

当一个套接字刚刚建立连接时,通常情况下发送缓冲区是有空间的。但是,为了确保应用程序能够及时响应写操作,通常的做法是在连接建立后立即设置写事件。这样做有几个好处:

  1. 立即响应:如果发送缓冲区确实有空间,那么设置写事件可以确保应用程序能够立即响应写操作。这有助于提高应用的响应速度。
  2. 预防缓冲区满的情况:即使当前缓冲区有空间,设置写事件可以防止未来缓冲区满时错过写事件。在 ET 模式下,如果没有设置写事件,当缓冲区满时,应用程序将不会收到 EPOLLOUT 事件,从而可能导致数据积压。

2. 在写操作被阻塞时设置写事件?

如果你在写操作被阻塞时才设置写事件,可能会导致以下问题:

  1. 事件丢失:在 ET 模式下,如果写操作被阻塞,并且在设置写事件之前缓冲区有了空间,那么这个事件可能会被丢失。因为 ET 模式只会报告一次事件,除非应用程序显式地清除事件状态。
  2. 延迟响应:如果在写操作被阻塞时才设置写事件,可能会导致响应延迟。因为此时缓冲区已经有空间了,但应用程序还没有设置写事件,所以不会立即得到通知。

3. 刚连接时设置写事件是否会立即触发一次写事件?

当一个套接字刚刚建立连接时,发送缓冲区通常有空间。在这种情况下,设置写事件确实可能会立即触发一次写事件。然而,这是预期的行为,处理起来比较简单。

异常事件逻辑:

        在epoll的ET模式下,想要主要触发异常事件(如:EPOLLERR ,EPOLLHUP),可以制造错误条件达到目的。比如通过主动close文件描述符,那么EPOLLHUP事件会被触发。

总结一下:

LT(水平触发):

  • EPOLLIN 触发条件:
       读缓冲区有数据就一直触发(即epoll_wait时能检测到),没有就不触发。

  • EPOLLOUT 触发条件:
        写缓冲区有空间可写,则一直触发。

ET(边缘触发)

EPOLLIN 触发条件:
    1. 当读 buff 从 空 -> 不空 时,触发;
    2. 当有新数据到达时,即读 buff 数据由 少 -> 多 时,触发;
    3. 当读 buff 有数据可读时,我们不处理,但是对相应fd进行epoll_ctl重新注册epoll_mod IN事件时,触发。

EPOLLOUT 触发条件:
    1. 当写 buff 从 满 -> 不满 时,触发;
    2. 当有数据被送走时,即写 buff 数据由 多 -> 少 时,触发;
    3. 当写 buff 有数据,但是我们没处理(没发送出去),但是对相应fd进行epoll_ctl重新注册epoll_mod OUT事件时,触发。

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获,请动动你发财的小手点个免费的赞,你的点赞和关注永远是博主创作的动力源泉。

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

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

相关文章

【kaggle竞赛】毒蘑菇的二元预测题目相关信息和思路求解代码

毒蘑菇的二元预测 您提供了很多关于不同二元分类任务的资源和链接,看起来这些都是Kaggle竞赛中的参考资料和高分解决方案。为了帮助您更好地利用这些资源,这里是一些关键点的总结: Playground Season 4 Episode 8 主要关注的竞赛: 使用银行…

如何将很多个pdf拼接在一起?很多种PDF拼接的方法

如何将很多个pdf拼接在一起?将多个PDF文件合并不仅能够提升信息的整合性,还能使文件管理更加高效。想象一下,你需要向同事或老师提交一份综合报告,其中包含了多份相关资料。如果每个文件单独存在,查找和传输都会变得繁…

Java 入门指南:JVM(Java虚拟机)—— Java 类加载器详解

类加载器 类加载器(Class Loader)是 Java 虚拟机(JVM)的一部分,它的作用是将类的字节码文件(.class 文件)从磁盘或其他来源加载到 JVM 中。类加载器负责查找和加载类的字节码文件,并…

Python|OpenCV-实现识别目标图像中的圆圈(20)

前言 本文是该专栏的第22篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 在处理图像检测项目的时候,可能会遇到需要检测目标图像中的“圆圈”需求。笔者在这里举个例子,如下图所示: 在图中有一个篮球,但是我们要找的目标对象并不是篮球,而是篮球它本身的这个…

MySQL安装教程

MySQL安装教程 如果需要删除原有mysql,然后安装过新的,可以参照如何彻底卸载旧mysql重装测试 1. 准备资源 mysql官网直达:https://dev.mysql.com/downloads/mysql/ CADN:https://download.csdn.net/download/luocong321/89592962 …

《深度学习》PyTorch框架 优化器、激活函数讲解

目录 一、深度学习核心框架的选择 1、TensorFlow 1)概念 2)优缺点 2、PyTorch 1)概念 2)优缺点 3、Keras 1)概念 2)优缺点 4、Caffe 1)概念 2)优缺点 二、pytorch安装 1、安装 2、…

【人工智能】在大型活动中的应用案例

人工智能在娱乐大型活动中的应用 ## 作者主页: 知孤云出岫 目录 **人工智能在娱乐大型活动中的应用****1. 引言****2. 智能票务与入场管理****2.1 动态定价与票务预测****2.2 生物识别技术快速入场****2.3 区块链技术防伪票务管理** **3. 智能观众互动与个性化体验****3.1 个性…

CCF csp认证 小白必看

c支持到C17(还是更高?);所以学一些封装好的函数功能是必要的---比如STL里的函数; 因为可携带纸质资料,建议打印带入,需要时可翻阅。 【题目概述:】 0-devc环境配置 配置好你常用的编译版本: 想要调试记得开启下选…

Python练习宝典:Day 1 - 选择题 - 基础知识

目录 一、踏上Python之旅二、Python语言基础三、流程控制语句四、序列的应用 一、踏上Python之旅 1.想要输出 I Love Python,应该使用()函数。 A.printf() B.print() C.println() D.Print()2.Python安装成功的标志是在控制台(终端)输入python/python3后,命令提示符变为: A.&…

Linux自主学习篇

用户及权限管理 sudo 是 "superuser do" 的缩写,是一个在类 Unix 操作系统(如 Linux 和 macOS)中使用的命令。它允许普通用户以超级用户(root 用户)的身份执行命令,从而获得更高的权限。 useradd…

数据飞轮:打造业务增长的持续循环

在当今数据驱动的世界中,企业必须利用数据的力量才能保持竞争力。然而,仅仅收集和分析数据是不够的;企业必须能够从他们的数据中创造一个持续增长的循环,才能保持成功。其中一种方法就是创建数据飞轮。接下来让我们来探讨一下什么…

网络高级day03(Http)

目录 【1】HTTP简介 【2】 HTTP特点 【3】 HTTP协议格式 1》客户端请求消息格式 1> 请求行 2> 请求头 3> 空行 4> 请求数据 2》服务器响应消息格式 【1】HTTP简介 HTTP协议是Hyper Text Transfer Protocol (超文本传输协议)的缩写&a…

Python_控制循环语句

if语句单分支结构的语法形式如下&#xff1a; 【操作】输入一个数字&#xff0c;小于10&#xff0c;则打印这个数字(if_test01.py)&#xff1a; num input("输入一个数字&#xff1a;") if int(num)<10: print("小于10的数&#xff1a;"num)条件表达式…

Shader 中的光源

1、Shader 开发中常用的光源属性 Unity当中一共支持四种光源类型&#xff1a; 平行光&#xff08;Directional&#xff09;点光源&#xff08;Point&#xff09;聚光灯&#xff08;Spot&#xff09;面光源&#xff08;Area&#xff09;— 面光源仅在烘焙时有用 不管光源类型到…

[网络层]-IP协议相关特性

IP协议 基本概念 主机 : 配有IP地址,但是不进行路由控制的设备路由器 : 既配有IP地址,又能进行路由控制节点: 主机和路由器的统称 协议头格式 4位版本(version):占四位,用于指定IP协议的版本,例如,使用IPv4,该字段就为44位首部长度: 表示IP协议首部的长度,以32位bit (4字节)…

Linux:终端(terminal)与终端管理器(agetty)

终端的设备文件 打开/dev目录可以发现其中有许多字符设备文件&#xff0c;例如对于我的RedHat操作系统&#xff0c;拥有tty0到tty59&#xff0c;它们是操作系统提供的终端设备。对于tty1-tty12使用ctrlaltF*可以进行快捷切换&#xff0c;下面的命令可以进行通用切换。 sudo ch…

【小bug】使用 RestTemplate 工具从 JSON 数据反序列化为 Java 对象时报类型转换异常

起因&#xff1a;今天编写一个请求时需要通过RestTemplate调用外部接口&#xff0c;获取一些信息&#xff0c;但是在获取了外部接口响应内容后&#xff0c;使用强制转换发现报了类型转换异常。之前也遇到过&#xff0c;但是没记录下来&#xff0c;今天又查了一遍……干脆记录一…

Springboot使用ThreadPoolTaskScheduler轻量级多线程定时任务框架

简介&#xff1a; Spring注解定时任务使用不是很灵活&#xff0c;如果想要灵活的配置定时任务&#xff0c;可以使用xxl-job 或者 quartz等定时任务框架&#xff0c;但是过于繁琐&#xff0c;可能成本较大。所以可以使用ThreadPoolTaskScheduler来灵活处理定时任务 ThreadPoolT…

【C++】二叉搜索树的底层以及实现

个人主页 文章目录 ⭐一、二叉搜索树的概念&#x1f680;二、二叉搜索树性能分析&#x1f3dd;️三、二叉搜索树的操作1. 插入2. 查找3. 删除4. 遍历节点 &#x1f384;四、二叉搜索树的实现&#xff08;K模型&#xff09;&#x1f389;五、二叉搜索树的应用1. K模型2. KV模型…

基于ACMEv2协议的免费SSL证书申请-支持Let‘s Encrypt/Google/ZeroSSL

项目&#xff1a;https://github.com/cook-code-jazor/acmex 非开源&#xff0c;使用webui管理证书的申请&#xff0c;所有文件本地化存储&#xff0c;支持windows/linux/osx。 证书申请直连ACMEv2服务商&#xff0c;没有任何中间接口&#xff0c;支持Lets Encrypt/Google/Ze…