c++应用网络编程之十一Linux下的epoll模式基础

一、epoll模式

在前面分析了select和poll两种IO多路复用的模式,但总体给人的感觉有一种力不从心的感觉。尤其是刚刚接触底层网络开发的程序员,被很多双十一千万并发,游戏百万并发等等已经给唬的一楞一楞的。一听说只支持一两千个并发,便心里感觉这玩意儿是不是在应付人。
这就得多说两句,其实所谓的百万千万并发,并不是说在一台机器上搞的。现在的网络服务端一般都是在云上或者能自己弹性扩容。另外大家需要搞清楚的,很多高并发,看上去非常多,但不是真正的在一秒两秒内完成,它会利用一个缓冲的时间进行最终一致性处理。换句话说,实际的应用工程上,大多在设计上采用一些方法来规避在单机上实现太多的并发。不过反回头来说,不是说单机上不能实现这么高的并发。这需要成本、设计以及开发和网络等的共同努力,并不划算。
需求是技术进步的最重要的推手,正是有上面提到的实际业务需求,才会倒推技术不断的提高并发量。再强的设计,也脱离不开底层网络技术的支持。如果一台单机只支持十个并发,那么如何弹性扩容也就成为了一种灾难的存在。也就是工程创新和技术创新需要协同作战。
高并发的要求,就使的Linux提供了epoll这种新的网络通信模式。可以把epoll当成一个稳定版的select和epoll优化版本,它提供了更强大的并发和更多的数据通信支持。另外,根据实际情况,到底是IO密集型还是CPU密集型或者是二者的混合,epoll提供了水平触发和边缘触发两种方式。不过,在实地看到的版本中,水平触发应用还是比较常见的。但不代表边缘触发用得少。
epoll解决了前面两种模式的两个大问题即文件描述符在用户空间和内核空间的拷贝和描述符控制的限制以及其太多引起的效率的急剧降低。

二、epoll模式的基础

这里要澄清一些问题,就是epoll是不是确实如大家想象的千百倍的提高了网络通信性能。答案可能出乎大家的意料,在一般的应用场景下,可能有很大的提高,但很多场景下,其实epoll未必如此,甚至可能都不如select。大家在网上看到的的连接几十万个甚至上百万个连接的例程,都只是单纯的连接,并没有业务处理或者说极其简单的打印一个类似“Hello”的通信。所以这种例程只是向大家证明,epoll是可以支持上百万个连接的(当然,这需要一系列参数的完善和修改,请参看前面的文章“Socket并发配置之一config的配置”)。
那么怎么判断孰优孰劣呢?还是那句老话,看场景和实际应用环境。比较总是要有限定条件的,不然哪里有什么包打天下的技术。如果单纯从并发的效率来看,如果在小活动连接并发的情况下,epoll几乎是碾压式的胜出select和poll等,看下面的图:
在这里插入图片描述

注意:图中增加了kqueue这种网络模型
这个图需要简单的说明一下,图中是限制了100个活跃连接的基础测试,每个连接进行1000次的读写操作。图中的横轴代表了Socket句柄的数量,纵轴代表了请求响应的时间。从测试结果来看,随着Socket句柄的增加epoll和kqueue几乎不受影响,但Select等两个则有一个明显的上升趋势。
也就是说,在这种场景下,epoll的优势是非常明显的,并且随着Socket句柄的不断的增加性能依然保持稳定,从而更优于Select和Poll。但是,如果活跃的并发是1000个,1万个甚至全都是呢?epoll的优势同样会丧失殆尽。
可实际情况比这种场景可能更复杂,比如客户端同时传输大文件(视频)等,所以实际的类似网络通信会在设计上规避这种响应逻辑或者使用其它的方式(比如类似P2P的方法)。
这种实际的情况告诉开发者,不要迷信技术,要实事求是的综合运用技术来解决问题。
好,从上面的分析,可以明显看出来epoll更适合高并发但瞬时活跃连接并不多的情况下,这种情况比较典型的就是IM及时通信。看上去并发非常多,但同一时间内活跃的并不多,比如一个人聊天,一般也就是和一两个人聊天,而且每次聊天是有间隔的。当然,这并不是说,IM开发简单,恰恰相反,由于其整体并发量的海量存在,使得其更难。或者说,实现一个简单的模型或者框架容易,但真正投入商用需要很长的路。
当然,在边缘触发的情况下,又增加了对大数据量的支持。

三、初步分析

epoll的通信分为边缘触发和水平触发两种情况:
1、水平触发
水平触发,Level Triggered即LT是默认的触发方式,只要在缓冲区内有数据可以读写操作时,epoll_wait则会触发事件,而且只要缓冲区内的数据没有被处理完成一直存在,则会一直触发此事件,直到数据处理完成。这样,就可以保证数据被上层应用准确完整的操作防止出现数据的丢失。但有得就有失,这种连续的触发事件(数据量大时会引起),必然会导致内核态和用户态的切换,降低效率。
特别是EPOLLOUT事件,为了防止连续触发(会产生大量不必要的通知)一定记得删除数据或删除此事件使用时再注册。
2、边缘触发
边缘触发,Edge Triggered即ET也很好理解,就是在发现缓冲区有可操作数据状态变换时,即触发事件,但这种事件只触发一次,直到下一次状态的变化。可能对于有一些硬件经验的开发者更容易理解,当一个电平被拉高时,会有一个上拉的曲线,在这个曲线到达一定位置后(上升沿),状态从0转成1,触发一些动作,然后一直持续到被拉低(下降沿)才会从1变成0.这时才会再次动作。这对于处理一些大数据的传输时很能提高效率,但缺点就是必须自己处理缓存中的所有数据直到确保数据已经操作完成。
注意,这个状态是内核控制的状态而不是用户操作的状态,对于读缓冲,是有新数据到添加到缓冲时触发;对于写缓冲则在是缓冲容量发生变化即内核删除确认的分组使空间空闲此时导致缓冲容易变化。
所以,在边缘触发的情况下,读状态下,必须尽快保证把数据读出,否则下次新增加的数据也不会触发事件(EPOLLIN);写状态下,特别是数据比较大时,数据未发送完成,需要动态的进行写事件(EPOLLOUT)的再次注册。否则无法再次发送。不过正常的情况下,一般对EPOLLOUT的处理比较宽松,只会在异常情况(比如缓冲区溢出)才注册此事件,从而控制再次发送的可能。
在早期的版本中,使用ET的方式,还可以在某些情况下避免惊群的效应。但在新的内核版本中,此种情况已经解决。

在前面Select和Poll的分析中可以知道,其对文件描述符是要进行一个全面的遍历的过程,这就意味着其的复杂度为O(n),而反观epoll则O(1),即其查找的时间是一个恒定的值。
原因就在于,epoll使用的是红黑树+双向链表,它分为为两种情况,一种是传入内核的文件描述符不变,则可以使用上次的结果;另外一种是变化,内核使用红黑树查找并修改双向链表。此时的查询仍然是O(1)。
另外,对于网上很多提及epoll使用了mmap这个事情,目前看源码中并未找到直接调用mmap的代码,可以说epoll并未使用mmap。但epoll确实借鉴了mmap的机制,实现了内核状态与用户状态的事件通知机制。有兴趣可以看一下代码fs/eventpoll.c中的源码。

四、总结

学习epoll可以明显看到技术栈升级的过程。这也再次证明,技术的发展,更多是在前面技术的基础上不断延革的,而技术革命本身就是一种小概率事件。技术革命更类似于基因突变,而正常的技术进步更类似于生物的自然进化。
万物相同,确实如此。

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

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

相关文章

阿里Dataworks使用循环节点和赋值节点完成对mongodb分表数据同步

背景 需求将MongoDB数据入仓MaxCompute 环境说明 MongoDB 100个Collections:orders_1、orders_2、…、orders_100 前期准备 1、MongoDB数据源配置 需要先保证DW和MongoDB网络是能够联通的,需要现在集成任务中配置MongoDB的数据源信息。 具体可以查…

Python OpenCV精讲系列 - 三维重建深入理解(十七)

💖💖⚡️⚡️专栏:Python OpenCV精讲⚡️⚡️💖💖 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计,从基础概念入手,逐步深入到图像处理、特征检测、物体识…

AD9361 在低至 1MHz 的频率下运行

AD9361 在低至 1MHz 的频率下运行 AD -FREQCVT1-EBZ是包含AD9361的FMCOMMS3/4/5板的附加板。虽然完整的芯片级设计包可在此 RF 收发器的ADI产品页面上找到,但有关此卡的信息及其使用方法、围绕它的设计包以及可使其工作的软件可在此处找到。 AD-FREQCVT1-EBZ 模块…

无人机之放电速率篇

无人机的放电速率是指电池在一定时间内放出其储存电能的能力,这一参数对无人机的飞行时间、性能以及安全性都有重要影响。 一、放电速率的表示方法 放电速率通常用C数来表示。C数越大,表示放电速率越快。例如,一个2C的电池可以在1/2小时内放…

储能电源自动化测试系统中不同硬件电路设计对测试结果有哪些影响?-纳米软件

随着能源领域的不断发展,储能电源在各个领域的应用越来越广泛。为了确保储能电源的性能和可靠性,自动化测试系统的重要性日益凸显。其中,硬件电路设计是自动化测试系统的关键组成部分,不同的硬件电路设计会对测试结果产生不同的影…

程序报错:ModuleNotFoundError: No module named ‘code.utils‘; ‘code‘ is not a package

程序报错内容&#xff1a; Traceback (most recent call last): File "code/nli_inference/veracity_prediction.py", line 10, in <module> from code.utils.data_loader import read_json ModuleNotFoundError: No module named code.utils; code is …

Linux运维_Apache更改默认网站目录

1.首先创建目录 并且在目录下新建测试文件 index.html mkdir -p /home/test/ap_web 直接wget 百度官网 wget www.baidu.com 2.编辑配置文件 /etc/apache2/sites-available/000-default.conf(找到 DocumentRoot)更改为刚刚创建的目录 接着在添加 最终文件: 3.给文件 添加属…

面试题:Redis(五)

1. 面试题 面试问 记录对集合中的数据进行统计 在移动应用中&#xff0c;需要统计每天的新增用户数和第2天的留存用户数&#xff1b; 在电商网站的商品评论中&#xff0c;需要统计评论列表中的最新评论&#xff1b; 在签到打卡中&#xff0c;需要统计一个月内连续打卡的用户数&…

【AI大模型】羊驼大模型详解_零基础入门到精通,看完这篇就足够了~

LLaMa系列模型 羊驼模型&#xff08;鼻祖是LLaMa模型&#xff0c;Facebook公司开源模型&#xff09;&#xff1a;即将成为大模型的安卓&#xff0c;国内95%的大模型都是羊驼套壳。GPT系列&#xff08;OpenAI公司&#xff09;&#xff1a;相当于大模型的iOS&#xff08;不开源&…

鸿蒙OS启动流程

启动流程(基于openharmony4.1) 系统上电加载内核后&#xff0c;按照以下流程完成系统各个服务和应用的启动&#xff1a; 内核加载init进程&#xff0c;一般在bootloader启动内核时通过设置内核的cmdline来指定init的位置。init进程启动后&#xff0c;会挂载tmpfs&#xff0c;…

JavaSE——泛型

目录 一、泛型的引入 二、泛型的好处 三、泛型介绍 四、泛型的语法 (一)泛型的声明 (二)泛型的实例化 五、泛型使用的注意事项和细节 六、泛型练习题1 七、自定义泛型 (一)自定义泛型类 (二)自定义泛型接口 (三)自定义泛型方法 八、泛型练习题2 九、泛型的继承和…

moe2024新生赛--pwn篇

moe2024新生赛–pwn篇 也算是复健吧。。 文章目录 moe2024新生赛--pwn篇**1 二进制漏洞审计入门指北**2 NotEnoughTime3 no_more_gets4 leak_sth5 ez_shellcode6 这是什么&#xff1f;libc7 这是什么&#xff1f;shellcode8 这是什么&#xff1f;random9 flag_helper10 这是什么…

PCB缺陷检测数据集 xml 可转yolo格式 ,共10688张图片

PCB缺陷检测数据集&#xff08;yolov5,v7,v8&#xff09; 数据集总共有两个文件夹&#xff0c;一个是pcb整体标注&#xff0c;一个是pcb部分截图。 整体标注有6个分类&#xff0c;开路&#xff0c;短路等都已经标注&#xff0c;标注格式为xml&#xff0c;每个文件夹下有100多张…

bp intruder 四种攻击类型 记录

1. Sniper 攻击&#xff08;狙击手模式&#xff09; 特点&#xff1a; Sniper 攻击是最基础的一种攻击类型&#xff0c;适用于单参数的简单测试。它会逐一替换每一个 payload 插入点&#xff0c;其他位置保持不变&#xff0c;从而测试单个参数对应用的影响。 工作流程&#…

Java-IO流使用场景

Java IO 流是Java编程中非常重要的组成部分,用于处理文件读写、网络通信等数据传输任务。 1. 字节流 1.1 读取文件 import java.io.FileInputStream; import java.io.IOException;public class ReadFileExample {public static void main(String[] args) {try (FileInputSt…

不用搭建服务?MemFire Cloud让开发更简单

不用搭建服务&#xff1f;MemFire Cloud让开发更简单 在当今的开发世界里&#xff0c;想要开发一个功能齐全的应用&#xff0c;往往意味着需要搭建复杂的后端、开发API接口、处理认证授权、管理数据库……这些琐碎的工作让很多开发者头疼不已&#xff0c;尤其是独立开发者或者…

成都睿明智科技有限公司电商服务可靠不?

在这个短视频风起云涌的时代&#xff0c;抖音不仅成为了人们娱乐消遣的首选平台&#xff0c;更是众多商家竞相追逐的电商新蓝海。成都睿明智科技有限公司&#xff0c;作为抖音电商服务领域的佼佼者&#xff0c;正以其独到的洞察力和专业的服务&#xff0c;助力无数品牌在这片沃…

【进阶OpenCV】 (16)-- 人脸识别 -- FisherFaces算法

文章目录 FisherFaces算法一、算法原理二、算法优势与局限三、算法实现1. 图像预处理2. 创建FisherFace人脸特征识别器3. 训练模型4. 测试图像 总结 FisherFaces算法 PCA方法是EigenFaces人脸识别的核心&#xff0c;但是其具有明显的缺点&#xff0c;在操作过程中会损失许多人…

程序员如何使用AI工具进行设计开发?

一、需求分析阶段 自然语言处理辅助理解需求&#xff1a; 使用自然语言处理工具&#xff0c;如 ChatGPT 等&#xff0c;将复杂的业务需求描述转化为更清晰的技术要求。例如&#xff0c;向 AI 解释项目的背景和目标&#xff0c;让它帮助梳理关键需求点和可能的技术挑战。通过与…

Docker下安装RabbitMQ

文章目录 Docker下安装RabbitMQ1. 下载Rabbitmq镜像2. 创建并运行RabbitMQ容器3. 查看启动情况4. 启动RabbitMQ访问的Web客户端4-1 方法一 进入容器开启4-2 方法二 直接开启5. 浏览器访问RabbitMQ的Web客户端页面6. Web客户端页面问题6-1 问题展示6-2 解决方案 Docker下安装Rab…