高级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接口的非阻塞例子
在优化web服务器为epollET,后面持续更新中...

结语

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

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

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

相关文章

ElasticSearch-2-核心语法集群高可用实战-Week2

ES批量操作 1.批量获取文档数据 这里多个文档是指,批量操作多个文档,搜索查询文档将在之后的章节讲解 批量获取文档数据是通过_mget的API来实现的 (1)在URL中不指定index和type 请求方式:GET 请求地址:_mget 功能说明 &#…

12 Java文件处理之写入、读取:IO流(中):高级流(缓冲流、转换流、序列化流和反序列化流、打印流)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、缓冲流1 字节缓冲流(1)BufferedInputStream:字节缓冲输入流构造方法---- BufferedInputStream(InputStream in):创建一个使用默认缓冲区大小的缓冲输入流。---- BufferedInputStream(In…

【浅水模型MATLAB】尝试复刻SCI论文中的溃坝流算例

【浅水模型MATLAB】尝试复刻SCI论文中的溃坝流算例 前言问题描述控制方程及数值方法浅水方程及其数值计算方法边界条件的实现 代码框架与关键代码模拟结果 更新于2024年9月17日 前言 这篇博客算是学习浅水方程,并利用MATLAB复刻Liang (2004)1中溃坝流算例的一个记录…

特殊文本文件日志技术重点笔记。

特殊文本文件,日志技术(黑马 一套入门 3h) 特殊文件 日志技术 把程序运行的信息,记录到文件中,方便程序员定位bug,并了解程序的执行情况等。 1.为什么要用这些特殊文件 1.1存储单个用户的: 用户名,密码 1.2存储多个用户的&…

数据清洗-缺失值填充-XGboost算法填充

目录 一、安装所需的python包二、采用XGboost算法进行缺失值填充2.1可直接运行代码2.2以某个缺失值数据进行实战2.2.1 代码运行过程截屏:2.2.2 填充后的数据截屏: 三、XGBoost算法原理介绍3.1 XGBoost 的定义3.2 XGBoost 的核心思想3.3 XGBoost 的特点3.…

2024 批量下载知乎回答/文章/想法/专栏/视频/收藏夹,导出 excel 和 pdf

之前分享过文章 2024批量下载知乎回答文章想法专栏收藏夹,公众号文章内容图片封面视频音频,微博内容图片视频评论转发数据,导出excel和pdf ,今天再整理分享下知乎知乎回答/文章/想法/专栏/视频/收藏夹下载。 苏生不惑 这个账号已…

Jenkins基于tag的构建

文章目录 Jenkins参数化构建设置设置gitlab tag在工程中维护构建的版本按指定tag的版本启动服务 Jenkins参数化构建设置 选择参数化构建: 在gradle构建之前,增加执行shell的步骤: 把新增的shell框挪到gradle构建之前, 最后保存 …

驱动器磁盘未格式化难题:深度剖析与恢复实践

驱动器磁盘未格式化的深层探索 在数据存储与管理的日常中,驱动器作为我们数字生活的基石,其稳定性直接关系到数据的安全与可用性。然而,当屏幕上赫然出现“驱动器中的磁盘未被格式化”的提示时,许多用户往往感到手足无措&#xf…

Linux 文件与目录操作命令详解

文章目录 前言创建文件1. touch2. vim 文件内容显示3. cat4. more5. less6. head7. tail 文件(目录)复制、删除和移动8. cp9. rm10. mv 压缩文件与解压缩11. gzip12. zip 和 unzip 创建目录13. mkdir 删除目录14. rmdir 改变工作目录15. cd16. pwd 显示目…

【C语言】联合体枚举的讲解

目录 ✨声明!!!: 联合体与结构体只有一个区别,那就是内存存储方式不同 💕1.联合体的声明 💕2.联合体内存的存储 💕3.联合体字节大小的计算 例题2: ✨4.枚举的声明…

全面掌握 Jest:从零开始的测试指南(下篇)

在上一篇测试指南中,我们介绍了Jest 的背景、如何初始化项目、常用的匹配器语法以及钩子函数的使用。这一篇篇将继续深入探讨 Jest 的高级特性,包括 Mock 函数、异步请求的处理、Mock 请求的模拟、类的模拟以及定时器的模拟、snapshot 的使用。通过这些技…

list从0到1的突破

目录 前言 1.list的介绍 2.list的常见接口 2.1 构造函数( (constructor)) 接口说明 2.2 list iterator 的使用 2.3 list capacity 2.4 list element access 2.5 list modifiers 3.list的迭代器失效 附整套练习源码 结束语 前言 前面我们学习…

一款源码阅读的插件

文章目录 进度汇报功能预览添加高亮标记高亮风格设置笔记颜色设置数据概览高亮数据详情 结尾 进度汇报 之前提到最近有在开发一个源码阅读的IDEA插件,第一版已经开发完上传插件市场了,等官方审批通过就可以尝鲜了。插件名称:Mark source cod…

防火墙——NAT

目录 NAT NAT分类 旧分类 新分类 NAT配置 源NAT​编辑 配置源NAT地址池​编辑 关于源NAT环路问题 环境如下​编辑 防火墙nat​编辑​编辑 路由器要配置指向11.0.0.0 网段的静态路由​编辑 测试​编辑 如果此时有外网用户直接pingNAT地址,则环路出现。​…

PAT甲级-1016 Phone Bills

题目 题目大意 顾客打长途电话计费,输出每月的账单。输入一行给出一天24小时的计费钱数,注意单位是美分,还要乘以0.01。接下来给出n条记录,每条记录都包括客户名,时间,状态。“on-line”是开始打电话的时间…

专题四_位运算( >> , << , , | , ^ )_算法详细总结

目录 位运算 常见位运算总结 1.基础位运算 2.给一个数 n ,确定它的二进制表示中的第 x 位是 0 还是 1 3.运算符的优先级 4.将一个数 n 的二进制表示的第 x 位修改成 1 5.将一个数n的二进制表示的第x位修改成0 6.位图的思想 7.提取一个数(n)二进…

如何优雅地处理返回值

我们已经知道了如何优雅的校验传入的参数了,那么后端服务器如何实现把数据返回给前端呢? 返回格式 后端返回给前端我们一般用 JSON 体方式,定义如下: {#返回状态码code:string, #返回信息描述message:string,#返回值data…

算法设计与分析(线性时间选择算法

目录 线性时间选择算法(QuickSelect)实现注意事项有可能出现的特殊情况:小结: 线性时间选择算法(QuickSelect)实现 线性时间选择算法 是快速排序算法的一个变种,用于在未完全排序的数组中找到第…

Next-ViT: 下一代视觉Transformer,用于现实工业场景中的高效部署

摘要 由于复杂的注意力机制和模型设计,大多数现有的视觉Transformer(ViTs)在实际的工业部署场景中,如TensorRT和CoreML,无法像卷积神经网络(CNNs)那样高效运行。这提出了一个明显的挑战&#x…

[Redis] Redis中的set和zset类型

🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…