个人对epoll的理解

news/2024/4/20 15:13:53/文章来源:https://blog.csdn.net/weixin_44548228/article/details/129226622

epoll模型

epoll是linux下多路复用IO接口select/poll的增强版本。

  它能显著减少程序在大量并发连接中只有少量活跃的情况下系统cpu利用率,其一:它不会复制文件描述符集合来传递结果而迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合。其二:epoll获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

  epoll除了提供select/poll 那种IO事件的水平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。其中,以有可读数据事件为例,水平触发就是只要缓冲区中有数据,epoll_wait就会响应,就会返回;而边沿触发就是不管缓冲区中是否有数据,只有当有数据传来事件发生时,epoll_wait才会响应返回。

epoll函数

epoll中共有三个函数:
epoll_create

int epoll_create(int size)创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值第二个参数表示动作,用三个宏来表示
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;
} epoll_data_t;struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};

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

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

   需要注意的是,调用epoll_ctl函数时,会发现这里第三个参数和第四个参数都需要设置一个fd,这是为什么呢?按道理来说,如果都表示与监听事件相关联的文件描述符,那么只需设置一个就可以了,为什么还需要设置两个fd呢?

  实际上,传入的第三个参数fd是真正意义上的与需要监听的事件相关联的文件描述符,发生的io事件也是在这个文件描述符上的;而第四个参数epoll_event中设置的fd意义则完全不一样了:这里的fd是放在成员联合体data中的,由于是联合体,说明最终epoll_event中fd肯定不是一种“属性”,它实际上是这个epoll_event对应的事件的一个标识,因为这里通过epoll_ctl传入的epoll_event,如果其对应的事件发生了,那么在epoll_wait返回的那个epoll_event就绪链表中肯定也会存在这个epoll_event,这里的fd就可以用来标识epoll_wait返回的epoll_event是不是当初epoll_ctl设置的那个epoll_event。

  举个例子,你完全可以在调用epoll_ctl时,把epoll_event中的fd设置为任意一个独一无二的值比如说999,当epoll_wait返回时,你就可以遍历就绪链表,看哪一个epoll_event的fd为999,那么就说明这个就绪的epoll_event就是当初epoll_ctl设置的那个epoll_event。

epoll_wait

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,
maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,
参数timeout是超时时间(毫秒,0会立即返回,-1将永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

select、poll、epoll对比

selectpollepoll
支持最大连接数1024(x86) or 2048(x64)由内核决定无上限,由硬件决定无上限,由硬件决定
I/O效率每次调用进行线性遍历,时间复杂度为O(N)每次调用进行线性遍历,时间复杂度为O(N)使用“事件”通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到rdllist里面,这样epoll_wait返回的时候我们就拿到了就绪的fd。时间发复杂度O(1)
fd拷贝每次select都拷贝每次poll都拷贝调用epoll_ctl时拷贝进内核并由内核保存,之后每次epoll_wait不拷贝

为什么ET模式下,需要将套接字设置为非阻塞式?

   LT模式是指只要缓冲区中有数据,epoll_wait就会返回,而在ET模式下,epoll_wait只有当新事件来临时才会返回。举个例子,对于一般的LT,在阻塞模式下,如果设置了read一次读取长度为1K,当对端发来2K的数据,read一次读取完1K后,由于是水平触发,那么epoll_wait会再次返回,然后read去读剩下的1K数据,这样就将2K数据全部读完;

   而对于ET模式,那么在第一次读完1K数据后,epoll_wait是不会返回的,要返回也是需要等到下一次对端数据传来,假设下一次对端数据再传2K过来,此时epoll_wait返回,read读到的实际上是上一次剩下来的1K,这显然是不行的。因此,如果read是阻塞的,为了让read一次性读完数据,那么就需要用循环让read读取,循环两次来把2K读完,但是2K读完之后呢?由于read是阻塞的,当缓冲区中没有数据,read就会一直卡在那等待数据,此时read不返回,也就无法调用epoll_wait,如果这个时候一个客户端发起连接请求,不就没人来响应了吗?因此,在ET模式下,是不应该设置套接字为阻塞的。

   另一方面,如果设置为非阻塞,此时让read循环读取,当没有数据可读的时候,read并不会阻塞在那里,此时read就会返回-1,表示数据读完了。

   可以见得,阻塞与非阻塞,关键就在于read函数是否在无数据可读的时候会立刻返回,如果能返回,那自然就能继续处理其他请求了,而如果不能,那么read就会一直阻塞在原处,而其他连接请求也就无法及时处理了。

   因此,对于ET模式,应当将套接字设置为非阻塞式。

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

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

相关文章

使用 docker 部署 MySQL 会导致数据丢失吗

2023年2月28日,今天下午电话面试 java 岗位,经过一些提问后,面试官问了一个问题,“那么你最近在关注什么方面的技术点呢?”,可能是我之前的回答不太理想,且说辞都是“不好意思,可能最…

0224多态

目录 一、多态的引入 二、方法的多态 一、重载 二、重写 三、对象的多态(核心) 四、应用实例 五、向上转型 六、向下转型 七、属性没有重写 八、练习题 第一题 第二题 一、多态的引入 通过主人给宠物喂食这个例子,说明多态的必要性&…

K_A13_002 基于STM32等单片机驱动干簧管传感器 串口与OLED0.96双显示

K_A13_002 基于STM32等单片机驱动干簧管传感器 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明模块工作原理:对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RC干簧管传感器模块1.2、STM32F103C8T6干簧管传感器模块五、基础知识学习与相关资料…

Python+Yolov5跌倒检测 摔倒检测 人物目标行为 人体特征识别

PythonYolov5跌倒检测 摔倒检测 人物目标行为 人体特征识别如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<PythonYolov5跌倒摔倒人体特征识别>>编写代码&#xff0c;代码整洁&…

数据结构前提知识

数据结构数据结构 个体的存储个体关系的存储算法对存储数据的操作程序数据结构算法衡量算法的标准时间复杂度&#xff1a;注意不是程序执行的时间&#xff0c;因为一个程序执行的时间取决于软硬件环境&#xff0c;不同的机器&#xff0c;执行的速度不一样&#xff0c;配置好的…

CVPR 2023 接收结果出炉!再创历史新高!录用2360篇!(附10篇最新论文)

点击下方卡片&#xff0c;关注“CVer”公众号AI/CV重磅干货&#xff0c;第一时间送达点击进入—>【计算机视觉】微信技术交流群2023 年 2 月 28 日凌晨&#xff0c;CVPR 2023 顶会论文接收结果出炉&#xff01;这次没有先放出论文 ID List&#xff0c;而是直接 email 通知作…

最好的 QML 教程,让你的代码飞起来!

想必大家都知道&#xff0c;亮哥一直深耕于 CSDN&#xff0c;坚持了好很多年&#xff0c;目前为止&#xff0c;原创已经 500 多篇了&#xff0c;一路走来相当不易。当然了&#xff0c;中间有段时间比较忙&#xff0c;没怎么更新。就拿 QML 来说&#xff0c;最早的一篇文章还是 …

Linux内核崩溃 dump调试

内核-crash(崩溃)&#xff0c;oops消息&#xff0c;dump oops &#xff08;也称 panic&#xff09;&#xff0c;称程序运行崩溃&#xff0c;程序崩溃后会产生oops消息。 应用程序或内核线程的崩溃都会产生oops消息&#xff0c;通常发生oops时&#xff0c;系统不会发生死机&a…

中文预训练大模型—文心Ernie技术原理

文心Ernie技术原理 一、背景技术 Ernie是基于Bert模型进行改进&#xff0c;基本模型是Transformer&#xff0c;Bert完成的预训练任务是&#xff1a;完形填空&#xff08;通过基本语言单元掩码&#xff09;&#xff1b;上下句预测。 Bert模型的缺陷是&#xff1a;只能捕获局部…

【Spark分布式内存计算框架——Spark Streaming】9. 获取偏移量 应用案例:百度搜索风云榜(上)

4.4 获取偏移量 当SparkStreaming集成Kafka时&#xff0c;无论是Old Consumer API中Direct方式还是New Consumer API方式获取的数据&#xff0c;每批次的数据封装在KafkaRDD中&#xff0c;其中包含每条数据的元数据信息。 文档&#xff1a;http://spark.apache.org/docs/2.4.…

Linux系统介绍及熟悉Linux基础操作

一、什么是Liunx Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff09;于1991年10月5日首次发布&#xff0c;它主要受到Minix和Unix思想的启发&am…

【图像处理】数字图像处理基础(分辨率,像素,显示...)

Table of Contents1.数字图像处理基础1.1 图像表示1.1.1 图像成像模型1.1.2 数字图像的表示a.图像采样b.图像灰度的量化c.算比特数1.2 分辨率1.2.1 空间分辨率1.2.2 灰度分辨率1.3 像素间的关系1.3.1 像素邻域a.4邻域b.4对角邻域c.8邻域1.3.2 像素邻接1.3.3 像素连通1.3.4 像素…

“速通“ 老生常谈的HashMap [实现原理源码解读]

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 HashMap 实现原理&&源码解读 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f…

【Leedcode】栈和队列必备的面试题(第二期)

【Leedcode】栈和队列必备的面试题&#xff08;第二期&#xff09; 文章目录【Leedcode】栈和队列必备的面试题&#xff08;第二期&#xff09;一、题目&#xff08;用两个队列实现栈&#xff09;二、思路图解1.定义两个队列2.初始化两个队列3.往两个队列中放入数据4.两个队列出…

对账平台设计

背景 随着公司业务的蓬勃发展&#xff0c;交易履约清结算业务的复杂性也在不断的增高&#xff0c;资金以及各种数据的一致性和准确性也变得越发重要。 以交易链路为例&#xff0c;存在着如下一些潜在的不一致场景&#xff1a; 订单支付成功了&#xff0c;但是订单状态却还是“…

JVM方法区详解有这篇就够了

1、方法区在哪里《Java虚拟机规范》中明确说明&#xff1a;“尽管所有的方法区在逻辑上是属于堆的一部分&#xff0c;但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。”但对于HotSpotJVM而言&#xff0c;方法区还有一个别名叫做Non-Heap&#xff08;非堆&#xff09…

机械键盘不只有轴体的区别!键帽高度也有些学问

键盘键帽的学问有很多&#xff0c;上篇文章中&#xff0c;笔者和大家聊了键帽的材质和耐油污的问题。 除此之外&#xff0c;键帽的高度和字符的印刷方式也有不同&#xff0c;对于多数机械键盘来说&#xff0c;会发现每一列键帽的倾斜角度都略有不同&#xff0c;使用起来可以减少…

Android TV UI开发常用知识

导入依赖 Google官方为Android TV的UI开发提供了一系列的规范组件&#xff0c;在leanback的依赖库中&#xff0c;这里介绍一些常用的组件&#xff0c;使用前需要导入leanback库。 implementation androidx.leanback:leanback:$version常用的页面 这些Fragment有设计好的样式&…

3.ffmpeg命令行环境搭建、ffmpeg命令行初步了解

在上章,我们讲过: ffmpeg.exe: 主要用于转码或者剪切的应用程序, 也可以从url/现场音频/视频源抓取输入源ffplay.exe: 主要用于播放视频的应用程序,该应用程序源码是开源的,我们后面章节会去源码分析ffprobe.exe: 主要用于分析视频码流的应用程序, 可以获取媒体文件的详细信息,…

【Azure 架构师学习笔记】-Azure Data Factory (4)-触发器详解-事件触发器

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Data Factory】系列。 接上文【Azure 架构师学习笔记】-Azure Data Factory (3)-触发器详解-翻转窗口 前言 事件触发指的是存储事件&#xff0c;所以在新版的ADF 中&#xff0c;已经明确了是“存储事件”&#xff0c;…