HighConcurrencyCommFramework c++通讯服务器框架 :Epoll:事件驱动技术

news2025/1/12 16:05:54

在单独的进程或者线程中运行,收集处理事件,没有上下文切换的消耗,高校;

写小demo很简单,正经让epoll技术融合到商业环境中,那么难度很大;

达到的效果:

1.理解工作原理;

2.开始写代码;

3.认可nginx epoll 源码;并且能复用;

四个函数对着源码讲

https://github.com/wangbojing/NtyTcp/blob/master/src/ntyepollrb.c

一位网友的手写源码

epoll_create();

epoll_ctl();

epoll_wait();

epolleventcallback();

学完再看;

epoll_create()
int epoll_create(int size); //size >0就行;
功能 :创建一个epoll对象,返回该对象的描述符,描述符就是epoll对象 epfd ,最后要用clsoe关闭

原理:
rbr:
  分配一个指针内存,代表红黑树的根节点,指向空;
  红黑树,用来保存,键【数字】、值【成员】对 一起的,查找速度特别快,能快速通过key值把value取出
rdist:
  代表一个双向链表的表头指针;遍历很快;

 总结:创建了一个eventpoll对象,被系统保存
       rbr初始化成一课红黑树的根
       rlist 成员初始化指向一个双向链表的根
epoll_ctl()
int epoll_ctl(int epfd,int op,int sockfd,struct epoll_event*event);
参数:
  epfd :epoll_create()返回的epoll文件描述符
  op :动作,添加,删除,修改,对应数字是1,2,3,EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
      添加事件:等于往红黑树中添加节点,每个客户端链接的服务器后,服务器都会产生一个对应的socket,每个连接这个socket都不重复,这个socket就是红黑树的key,把节点添加到红黑树上去;   
      修改事件:你用了add之后才可以修改
      删除动作:是从红黑树把这个节点制空,这回导致这个socket这个TCP连接上无法收到任何系统通知事件;
  sockfd:文件描述符
  event:事件信息;

 红黑树的节点是由epoll_ctl()add 
epoll_wait()
int epoll_wait(int epfd,struct epoll_event*events,int maxevent,int timeout)
系统将可读事件通过回调函数将变化的套接字加入到双向链表里,然后链表,系统将可读事件拷贝回用户空间的集合返回总个数,用户来遍历,都是已经经过变化的连接直接

epoll 通过两个方面,很好解决了 select/poll 的问题。

第一点,epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket 的数据结构,所以 select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。

第二点, epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。

从下图你可以看到 epoll 相关的接口作用:

https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost4@main/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/epoll.png

epoll 的方式即使监听的 Socket 数量越多的时候,效率不会大幅度降低,能够同时监听的 Socket 的数目也非常的多了,上限就为系统定义的进程打开的最大文件描述符个数。

epoll函数实战

epollcreate(),epollctl() ,epoll_wait();

总结:

ngxgetconnection()重要函数;从连接池找空闲连接

ngxepollinit()调用;在子进程执行;

ngxepolladd_event();

连接池:

首先连接池维护两个列表一个是空闲连接列表,一个是全部的(无论是否空闲都放在这里),一开始这两个列表是一样大的,大小都为最大连接数;首先我们监听套接字在监听,epoll将监听事件添加,当有新的连接时,此时连接池的空闲连接(未被使用的对象)分配出来,getconn(cfd),将这个连接绑定到这个对象中,并且将这个对象从队列里取出;

LT | ET

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;

  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

事件驱动框架:和nginx框架差不多

就是有一些事件发生源(三次握手内核通知,事件发生源就是客户端),通过事件收集器收集和分发事件

事件收集器(epoll_wait())产生事件

分发事件:就是调用函数

事件处理器(waitrequesthandler(),event_accept())用来消费事件都不为阻塞函数;

腾讯后台开发的面试题?

使用linux epoll模型的水平触发模式时,当socket可写时,也就是写缓冲区没满时,那么会不停的触发socket可写事件通知你,如何处理?

1.要写的时候再把这个socket加入到epoll事件中去,等待可写事件,接到可写事件时调用write或者send发送数据,当所有数据都写完时,将socket移除

  缺点:即使发送数据很小,那也要添加上去,写完还要移除epoll,有一定的操作代价

2.

一种改进的万式:

开始不把socket加入epoll,需要向socket写数据的时候,直接调用write或者send发送数据。如果返回EAGAIN,把socket加入epoll,在epoll的驱动下写数据,全部数据发送完毕后,再移出epoll。这种方式的优点是:数据不多的时候可以避免epo11的事件处理,提高效率。

直接写然后如果能写就写了,不能写代表满了,出现错误之后,在进行epoll检测是否没满(类似于乐观锁)

对于非阻塞的套接字:如果缓冲区没有数据,他会返回-1 errno==EAGAIN,如果返回这个,那么说明数据已经读完或者接收完;

ET LT触发的事件来临,有事件来了通知不一样

LT :模式下,只要读缓冲区有数据,那么双向链表一定会把这个事件在加进来,没读完就得反复加进来(只要正常读完数据效率也会加大)

ET:模式下,只要有新事件,从不可读到可读事件来,操作系统就会把这个可读事件加到链表里,然后通知你一次,不关你读没读,这个事件就被链表删除了,想要在读,只能再触发。

如何选择ET LT :

如果收发数据包有固定格式,采取LT模式,简单清晰,写好效率也很高;

准备LT 这种方法【采用固定格式的数据收发方式来写这个项目】

如果收发数据包没有固定格式,可以考虑采用ET格式;

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

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

相关文章

Splay学习笔记

Splay的两个关键函数,rotate和splay rotate就是正常的旋转。 splay(x,target)表示把x旋转为target的子节点 这里需要分讨,对于x的父亲y和祖父z 若 z target, 则直接转x若 x 与 y 方向相同,先转y,后转x若 x 与 y 方…

html+css网页制作 电商华为商城首页 ui还原度100%

htmlcss网页制作 电商华为商城首页 ui还原度100% 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码…

Docker日志管理

一、知识点介绍 1.ELK(Elasticserach、Logstash、Kibana) 前面笔记有 2.什么是 Filebeat Filebeat 是 ELK 组件的新成员, 也是 Beat 成员之一。基于 Go 语言开发,无任何依赖并且比 Logstash 更加轻量,不会带来过高的资源占用, …

django常用的组合搜索组件

文章目录 django常用的组合搜索组件快速使用配置信息1. 视图函数2. 前端模板3. css样式 代码实现 django常用的组合搜索组件 在项目开发中,如果有大量数据就要用到组合搜索,通过组合搜索对大块内容进行分类筛选。 快速使用 三步走:&#xf…

刷题记录第110天-分割等和数组

解题思路: 问题可转化为,用给定数组能否装满一个容量为数组总和一半的背包(targetsum/2),即一个0-1背包问题。 0-1背包问题的关键在于数组的定义和状态转移方程以及价值的定义。dp[i][j]表示在[0…i]个物品内,背包容量为j能装的最…

再升级!MoneyPrinterPlus集成GPT_SoVITS

最近有很多优秀的语音合成TTS工具,目前MoneyPrinterPlus已经集成了ChatTTS和fasterWhisper。应朋友们的要求,最近MoneyPrinterPlus也集成了GPT_SoVITS这个优秀的语音合成工具。 今天给大家详细讲解一下,如何在MoneyPrinterPlus中使用GPT_SoV…

机器学习速成第三集——无监督学习之降维(理论部分)!

目录 主成分分析(PCA) 独立成分分析(ICA) t分布随机邻近嵌入(t-SNE) 线性判别分析(LDA) 其他降维方法 应用场景 主成分分析(PCA)在处理大规模数据集时…

新能源汽车电机低频电磁场仿真应用

一、背景介绍 随着新能源汽车的普及,电机作为新能源汽车驱动系统的核心组成部分,其重要性不言而喻。电机使电能转化为机械能,通过传动系统将机械能传递到车轮,驱动汽车行驶。新能源汽车电机的发展经历了从初步探索到技术成熟的多…

Localization Translate API 的对接和使用

Localization Translate API 的对接和使用 Localization Translate API 的主要功能是通过输入需要翻译的文本来获取翻译后的文本,同时翻译后的语言可以自定义,并且翻译结果可以采用 json , markdown 俩种主流的方法来输出。 本文档将详细介…

【安卓】多线程编程

文章目录 线程的简单应用解析异步消息处理机制使用AsyncTask 线程的简单应用 新建一个AndroidThreadTest项目&#xff0c;然后修改activity_main.xml中的代码。 <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width…

CNN-GRU神经网络多输入单输出回归预测【MATLAB】

1. CNN&#xff08;卷积神经网络&#xff09;部分 作用&#xff1a; 特征提取&#xff1a;CNN擅长从输入数据中提取空间特征。在多输入情况下&#xff0c;它可以处理来自不同源的数据&#xff0c;提取有用的特征。 局部感受野&#xff1a;通过卷积操作&#xff0c;CNN能够识别…

【ACM出版,往届会后三个半月EI见刊/检索】第四届物联网与机器学习国际学术会议(IoTML 2024,8月23-25)

2024年第四届物联网与机器学习国际学术会议&#xff08;IoTML 2024&#xff09;将于2024年8月23-25日在中国南昌召开。 会议将围绕着物联网和机器学习开展&#xff0c;探讨本领域发展所面临的关键性挑战问题和研究方向&#xff0c;以期推动该领域理论、技术在高校和企业的发展和…

vector嵌套之空指针异常

文章目录 1. 题目链接2. 题目代码正确代码错误代码 1. 题目链接 118. 杨辉三角 2. 题目代码 正确代码 class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>> result(numRows);for(int i 0; i < numRows; i)…

STL中的栈(stack)和队列(queue)以及简单(复用)实现

适配器&#xff1a; 虽然 stack 和 queue 中也可以存放元素&#xff0c;但在 STL 中并没有将其划分在容器的行列&#xff0c;而是将其称为 容器适配器 &#xff0c;这是因为 stack 和队列只是对其他容器的接口进行了包装&#xff0c; STL 中 stack 和 queue 默认使用deque 换种…

【云备份】学习Json

文章目录 1.Json数据类型基础数据类型复合数据类型JSON数据类型的应用 2.学习jsoncpp库利用json实现序列化利用json实现反序列化 1.Json数据类型 json 是一种数据交换格式&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。json数据交换格式是将多种数据对象组织…

CVE-2024-38077 Windows远程桌面授权服务漏洞介绍

CVE-2024-38077 是一个在Windows远程桌面授权服务&#xff08;Remote Desktop Licensing Service&#xff09;中存在的严重远程代码执行漏洞。以下是关于此漏洞的详细信息&#xff1a; 漏洞概述 漏洞编号&#xff1a;CVE-2024-38077漏洞类型&#xff1a;远程代码执行 (RCE)影…

基于单片机控制的多功能智能语音风扇

【摘要】 本文简述了一种基于单片机控制的智能多功能语音风扇的设计&#xff0c;该设计以STC11L08XE单片机为主控制器&#xff0c;通过YS-LDV7语音模块对语音信号进行采集识别&#xff0c;并将该信号上传给单片机进而控制风扇的转速和开关&#xff0c;以达到语音控制的效果。该…

Python 安装 PyTorch详细教程

本章教程,介绍如何安装PyTorch,介绍两种安装方式,一种是通过pip直接安装,一种是通过conda方式安装。 一、查看CUDA版本 二、安装PyTorch 1、pip安装方式 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu1162、conda安装方式 …

Leetcode—3151. 特殊数组 I【简单】

2024每日刷题&#xff08;155&#xff09; Leetcode—3151. 特殊数组 I 实现代码 class Solution { public:bool isArraySpecial(vector<int>& nums) {int n nums.size();for(int i 1; i < n; i) {if(nums[i - 1] % 2 nums[i] % 2) {return false;}}return t…

【数据结构-前缀哈希】力扣1124. 表现良好的最长时间段

给你一份工作时间表 hours&#xff0c;上面记录着某一位员工每天的工作小时数。 我们认为当员工一天中的工作小时数大于 8 小时的时候&#xff0c;那么这一天就是「劳累的一天」。 所谓「表现良好的时间段」&#xff0c;意味在这段时间内&#xff0c;「劳累的天数」是严格 大…