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

      • `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.rhkb.cn/news/381315.html

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

相关文章

艺术成分很高的完全自定义的UITabBar(很简单)

引言 在iOS应用开发中&#xff0c;UITabBar是一个非常场景且重要的UI组件。系统为我们提供的UITabBar虽然功能强大&#xff0c;但是在某些情况下&#xff0c;它的标准样式并不能满足我们特定的设计需求&#xff0c;它的灵活性也有一些局限。为了打造更具个性化好的用户友好的交…

【细如狗】记录一次使用MySQL的Binlog进行数据回滚的完整流程

文章目录 1 事情起因2 解决思路3 利用binlog进行数据回滚3.1 确认是否启用Binlog日志3.2 确认是否有binlog文件3.3 找到误操作的时间范围3.4 登录MySQL服务器查找binlog文件3.4.1 查询binlog文件路径3.4.2 找到binlog文件3.4.3 确认误操作被存储在哪一份binlog文件中 3.5 查看二…

【单片机毕业设计选题24074】-基于阿里云的空气质量监控系统

系统功能: 手机开启2.4G WiFi热点后再给系统上电 系统操作说明&#xff1a; 上电后OLED显示 “欢迎使用空气监控系统请稍后”&#xff0c;两秒后显示Connecting...表示 正在连接阿里云&#xff0c;正常连接阿里云后显示第一页面&#xff0c;如长时间显示Connecting...请 检…

初识C++|模板初阶

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; 目录 &#x1f349;1. 泛型编程 &#x1f349;2. 函数模板 &#x1f95d;2.1 函数模板概念 &#x1f95d;2.2 函数模板格式 &#x1f95d;2.3 函数模板的原理 &#x1f95…

elementUI在手机端使用遇到的问题总结

之前的博客有写过用vue2elementUI封装手机端选择器picker组件&#xff0c;支持单选、多选、远程搜索多选&#xff0c;最终真机调试的时候发现有很多细节样式需要调整。此篇博客记录下我调试过程中遇到的问题和解决方法。 一、手机真机怎么连电脑本地代码调试&#xff1f; 1.确…

Android 11 HAL层集成FFMPEG

1.集成目录&#xff1a; android/vendor/noch/common/external/NoboMediaCodec 2.文件夹目录 3. Android.mk实现 # Copyright #LOCAL_PATH : $(call my-dir)SF_COMMON_MK : $(LOCAL_PATH)/common.mkinclude $(call first-makefiles-under,$(LOCAL_PATH))4.common.mk实现 # #…

视觉巡线小车——STM32+OpenMV(三)

目录 前言 一、OpenMV代码 二、STM32端接收数据 1.配置串口 2.接收数据并解析 总结 前言 通过视觉巡线小车——STM32OpenMV&#xff08;二&#xff09;&#xff0c;已基本实现了减速电机的速度闭环控制。要使小车能够自主巡线&#xff0c;除了能够精准的控制速度之外&#xff0…

微信被好友屏蔽朋友圈/拉黑/删除?教你几招悄悄验证

微信这一国民级的社交软件&#xff0c;基本上渗入了大家日常生活的方方面面&#xff0c;沟通、支付、购物、娱乐都可以在上面一站式解决。微信功能虽然很全面&#xff0c;但某些功能细节设计也会让人感到困惑&#xff0c;比如我们被朋友拉黑或者删除&#xff0c;微信是不会通知…

数学建模--优劣解距离法TOPSIS

目录 简介 TOPSIS法的基本步骤 延伸 优劣解距离法&#xff08;TOPSIS&#xff09;的历史发展和应用领域有哪些&#xff1f; 历史发展 应用领域 如何准确计算TOPSIS中的理想解&#xff08;PIS&#xff09;和负理想解&#xff08;NIS&#xff09;&#xff1f; TOPSIS方法在…

【NLP自然语言处理】基于BERT实现文本情感分类

Bert概述 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种深度学习模型&#xff0c;用于自然语言处理&#xff08;NLP&#xff09;任务。BERT的核心是由一种强大的神经网络架构——Transformer驱动的。这种架构包含了一种称为自注…

宝塔SSL续签失败

我有2个网站a和b&#xff08;文字中用baidu.com替换我的域名&#xff09; b是要续签那个&#xff0c;但续签报错&#xff1a; nginx version: nginx/1.22.1 nginx: [emerg] host not found in upstream "github.com" in /www/server/panel/vhost/nginx/proxy/a.bai…

分享 2 个 .NET EF 6 只更新某些字段的方法

前言 EF 更新数据时&#xff0c;通常情况下&#xff0c;是更新全部字段的&#xff0c;但实际业务中&#xff0c;更新全部字段的情况其实很少&#xff0c;一般都是修改其中某些字段&#xff0c;所以为了实现这个目标&#xff0c;很多程序员通常会这样作&#xff1a; 先从数据库…

Nginx 怎样处理请求的熔断机制?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 Nginx 怎样处理请求的熔断机制&#xff1f;一、什么是熔断机制二、Nginx 中的熔断机制原理&#xff08;一&#xff09;基于错误率&#xff08;二&#xff09;基于…

超级写手:AI笔耕者的未来图谱

在数字化时代,人工智能(AI)正悄然改变着各行各业的传统作业方式。其中,“超级写手”——AI scribes,作为一种新兴的垂直应用场景,正以其独特的魅力吸引着投资者的目光。本文将深入探讨AI写手的市场背景、技术栈、投资策略及其潜在应用领域,带您一窥这个未来写作助手的广…

动手学深度学习——5.卷积神经网络

1.卷积神经网络特征 现在&#xff0c;我们将上述想法总结一下&#xff0c;从而帮助我们设计适合于计算机视觉的神经网络架构。 平移不变性&#xff08;translation invariance&#xff09;&#xff1a;不管检测对象出现在图像中的哪个位置&#xff0c;神经网络的前面几层应该对…

Docker构建LNMP环境并运行Wordpress平台

1.准备Nginx 上传文件 Dockerfile FROM centos:7 as firstADD nginx-1.24.0.tar.gz /opt/ COPY CentOS-Base.repo /etc/yum.repos.d/RUN yum -y install pcre-devel zlib-devel openssl-devel gcc gcc-c make && \useradd -M -s /sbin/nologin nginx && \cd /o…

解决 go 引用私有包,安装失败

问题描述 go mod tidy 或者 go run main.go 时&#xff0c;提示失败&#xff0c;例如 no such host&#xff08;设置GOPRIVATE&#xff09;或者 x509: certificate signed by unknown authority 之类的报错&#xff08;设置GOINSECURE&#xff09; 解决 在各种 insteadof 方…

Android音视频—OpenGL 与OpenGL ES简述,渲染视频到界面基本流程

文章目录 OpenGL 简述特点和功能主要组件OpenGL ES当前状态 OpenGL ES 在 Android 上进行视频帧渲染总体流程 OpenGL 简述 OpenGL&#xff08;Open Graphics Library&#xff09;是一个跨平台的、语言无关的应用程序编程接口&#xff08;API&#xff09;&#xff0c;用于开发生…

通过albumentation对目标检测进行数据增强(简单直接)

albumentation官方文档看不懂&#xff1f;xml文件不知道如何操作&#xff1f;下面只需要修改部分代码即可上手使用 要使用这个方法之前需要按照albumentation这个库还有一些辅助库,自己看着来安装就行 pip install albumentation pip install opencv-python pip install json…

昇思25天学习打卡营第25天 | RNN实现情感分类

学习心得&#xff1a;RNN实现情感分类 在自然语言处理&#xff08;NLP&#xff09;的领域中&#xff0c;情感分类是一个极具挑战性的任务&#xff0c;它要求模型能够准确地从文本中识别出情感倾向。通过使用MindSpore框架和RNN模型进行情感分类&#xff0c;我获得了许多有关构…