Linux高级IO——多路转接之epoll

本章代码Gitee地址:EpollServer

文章目录

    • 1. epoll接口
      • 1.1 epoll_create
      • 1.2 epoll_wait
      • 1.3 epoll_ctl
    • 2. epoll原理
    • 3. epoll_server
    • 4. epoll两种工作模式

1. epoll接口

1.1 epoll_create

#include <sys/epoll.h>
int epoll_create(int size);

参数int size理论上可以随便写(已废弃)

返回值:

  • 成功返回一个文件描述符
  • 失败返回-1

1.2 epoll_wait

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

参数:

  • int epfdepoll_create的返回值

  • struct epoll_event *events, int maxevents:用户及缓冲区,返回已经就绪的文件描述符和事件

    typedef union epoll_data {void    *ptr;int      fd;uint32_t u32;uint64_t u64;
    } epoll_data_t;struct epoll_event {uint32_t     events;    //位图传递epoll_data_t data;      //
    };
    
  • int timeout:超时时间,单位是毫秒,0为非阻塞,-1为阻塞式

返回值:已经就绪的文件描述符的个数

1.3 epoll_ctl

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:

  • int epfdepoll_create的返回值

  • int op

    EPOLL_CTL_ADD	//增添
    EPOLL_CTL_MOD	//修改
    EPOLL_CTL_DEL	//删除
    
  • int fd, struct epoll_event *event:哪个文件描述符上的哪个事件

selectpoll都是用数组维护的,需要用户进行管理

2. epoll原理

网卡是外设,当硬件就绪之后,会以硬件中断的方式来告诉操作系统,将网卡的数据读到网卡驱动上,而操作系统读数据是从文件缓冲区读取数据。

所以为了支持epoll,操作系统支持三种机制:

  1. 内核会维护一颗红黑树,红黑树节点里面包含:

    struct rb_node
    {int fd;	//内核要关系的文件描述符uint32_t event;	//要关系的事件	位图形式//...
    }
    
  2. 此外还会维护一个就绪队列,一旦红黑树上有节点就绪,此时就会将该节点链入到队列当中

struct list_node
{int fd;	//已就绪的文件描述符uint32_t event;	//已就绪的事件//...
}
  1. 操作系统的底层网卡,是允许操作系统注册一些回调机制。

    操作系统内部提供一个回调函数,网卡以中断的方式将数据搬到了网卡驱动层,驱动层当中有数据就绪了,那么数据链路层就会自动调用对应的回调函数。
    这个回调函数要做的就是:

    • 向上交付
    • 数据到来解包交到tcp接收队列
    • 查找rb_tree->fd
    • 构建就绪节点,插入就绪队列

以上三套机制,就叫做epoll模型

Linux一切接文件,strcut file指针指向这个epoll模型,然后将struct file对象添加到进程文件描述符表里面,所以epoll的返回值是一个文件描述符。

image-20240412232048177

epoll优势:

  1. 检测就绪时间复杂度为O(1),判断队列是否为空

    获取就绪队列时间复杂度O(n)

  2. fdevent没有上限,所以的文件描述符和关系的事件都是由红黑树管理的,这颗红黑树多大,操作系统决定

如何看待这颗红黑树?

selectpoll都需要辅助数组,数组用户维护,而这颗红黑树就相当于之前我们自己维护的数组,只不过在epoll里面是由系统管理

  1. epoll_wait返回值表示有多少事件就绪,将就绪的节点一个一个弹出,依次放入数组,就绪事件是连续的

3. epoll_server

#include<iostream>
#include<memory>
#include<string>
#include"Socket.hpp"
#include"Log.hpp"
#include"Epoller.hpp"
#include"Nocopy.hpp"uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);class EpollServer : public Nocopy
{static const int defaultnum = 64;   //默认一次性最多获取64个事件
public:EpollServer(uint16_t port):_port(port),_listensock_ptr(new MySocket()),_epoller_ptr(new Epoller()){}void Init(){//创建套接字_listensock_ptr->Socket();//绑定套接字_listensock_ptr->Bind(_port);//监听套接字_listensock_ptr->Listen();log(Info, "create listen socket success: %d", _listensock_ptr->Getfd());}void Accepter(){// 获取新链接std::string clientip;uint16_t clientport;int sock = _listensock_ptr->Accept(&clientip, &clientport);if (sock > 0){// 不能直接读取,获取连接不代表发送了数据// 让epoll去关心_epoller_ptr->EpollerCtl(EPOLL_CTL_ADD, sock, EVENT_IN);log(Info, "get a new link, clientip: %s, clientport: %d", clientip.c_str(), clientport);}}void Recver(int fd){// 读事件就绪char buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1);   //BUGif (n > 0){buffer[n] = 0;std::cout << "get a message: " << buffer << std::endl;//返回std::string echo_str = "server echo $";echo_str += buffer;write(fd, echo_str.c_str(), echo_str.size());}else if (n == 0){log(Info, "client quit, me too, close fd:%d", fd);//从epoll当中移除   删除红黑树节点_epoller_ptr->EpollerCtl(EPOLL_CTL_DEL, fd, 0);close(fd);  //细节  先移除再关闭}else{log(Warning, " read error, close fd:%d", fd);}}void Dispatcher(struct epoll_event revs[], int num){for(int i = 0; i < num; i++){uint32_t events = revs[i].events;int fd = revs[i].data.fd;if(events & EVENT_IN){//读事件就绪if(fd == _listensock_ptr->Getfd()){Accepter();}else{//其他事件就绪Recver(fd);}}else if(events & EVENT_OUT){//写事件就绪}}}void Start(){//listensock套接字添加进epoll当中//listensock和它关心的事件    本质上添加到内核epoll模型的rb_tree里面_epoller_ptr->EpollerCtl(EPOLL_CTL_ADD, _listensock_ptr->Getfd(), EVENT_IN);    //关心读事件struct epoll_event revs[defaultnum];    //存放就绪的事件for(; ;){//epoll只负责等待int n = _epoller_ptr->EpollerWait(revs, defaultnum);if(n > 0){//有事件就绪log(Debug, "event happend, fd is : %d", revs[0].data.fd);//提取就绪事件   epoll_wait返回值会返回就绪的事件数量//如果数量大于定义的大小, 下次再捞Dispatcher(revs, n);  }else if(n == 0){log(Info, "time out...");}else{log(Error, "epoll_wait error");}}}~EpollServer(){_listensock_ptr->Close();}
private:std::shared_ptr<MySocket> _listensock_ptr;std::shared_ptr<Epoller> _epoller_ptr;//MySocket _listensock;uint16_t _port;//Epoller _epoller;
};

4. epoll两种工作模式

LT模式:

epoll默认工作模式是LT(Level Triggered水平触发)模式

当事件到来时,如果上层一直不取走,底层会一直通知

selectpoll采用的也是LT模式

EL模式:

EL(Edge Triggered边缘触发)模式是当数据变化的时候,才会通知一次

数据从无到有,从少到多

打个比方:

快递员A(LT模式)送快递的时候,如果客户一直不取,他就一直打电话,说你的快递到了,签收一下;

快递员B(ET模式)送快递的时候,只通知一次,然后就放在驿站了;如果之后又有快递到了,则又通知一次;

快递员A在一个小时只能,可能只能通知到几个客户;而快递员B在一个小时之内可以通知多个客户

ET不止通知效率高于LTIO效率也高于LT

由于ET只通知一次,所以就倒逼上层,每次都要把本轮数据全部取走

  • 如何知道本轮数据全部取完?
    比如说,我们有550g的大米,每天要吃100g,前5天正常,到第6天的时候,原本是要吃50g大米的,可是只能吃50g了,这就说明大米没有了
    也就是说当需要读取的目标数据大于实际读取的数据的时候,就表明数据已经全部取走。
    这就需要我们循环读取数据,直到读取出错为止,可是fd是默认是阻塞的,所以在ET模式下,所有的fd要设置成非阻塞Non_block,如果不设置,程序会一直阻塞住

    每次都能取走全部的数据,接收缓冲区就有空间了,这样tcp就能给对方通知更大的窗口,然后对方就可以给我们发送更多的数据

ET是否一定比LT高效?

LT所有的fd设置成non_block非阻塞,然后循环读取,这就个ET类似了

这里所谓的通知一次和每次通知,本质上其实是向就绪队列添加一次还是每次都添加

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

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

相关文章

微信小程序 uniapp+vue城市公交线路查询系统dtjl3

小程序Android端运行软件 微信开发者工具/hbuiderx uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 前端&#xff1a;HTML5,CSS3 VUE 后端&#xff1a;java(springbootssm)/python(flaskdja…

2011年认证杯SPSSPRO杯数学建模B题(第一阶段)生物多样性的评估全过程文档及程序

2011年认证杯SPSSPRO杯数学建模 B题 生物多样性的评估 原题再现&#xff1a; 2010 年是联合国大会确定的国际生物多样性年。保护地球上的生物多样性已经越来越被人类社会所关注&#xff0c;相关的大规模科研和考察计划也层出不穷。为了更好地建立国际交流与专家间的合作&…

文献学习-32-新生儿皮质表面重建的条件时间注意网络

Conditional Temporal Attention Networks for Neonatal Cortical Surface Reconstruction Authors: Qiang Ma, Liu Li, Vanessa Kyriakopoulou, Joseph V. Hajnal, Emma C. Robinson, Bernhard Kainz, and Daniel Rueckert Source: MICCAI 2023 Abstract 皮层表面重建在模拟…

数学杂谈之四:学习数学的方法

数学杂谈之四&#xff1a;学习数学的方法 数学杂谈之一&#xff1a;数学的形态 https://blog.csdn.net/cnds123/article/details/137437208 数学杂谈之二&#xff1a;数学中的概念和理解 https://blog.csdn.net/cnds123/article/details/137500537 数学杂谈之三&#xff1a;…

UTONMOS元宇宙游戏特点

在元宇宙的世界里&#xff0c;游戏不再只是一种娱乐方式&#xff0c;而是一种全新的生活体验。UTONMOS元宇宙游戏带你穿越虚拟与现实的边界&#xff0c;开启一段前所未有的冒险之旅。 在这个充满无限可能的UTONMOS元宇宙游戏中&#xff0c;你将成为自己游戏世界的主角。可以自…

如何恢复未保存或删除/丢失的Word文件?

关闭 Word 应用程序而不保存文档&#xff1f;误删Word文档&#xff1f;许多用户会在不同的情况下丢失Word文档。如果不幸遇到此类问题&#xff0c;如何恢复已删除或未保存的 Word 文档&#xff1f;有一些方法可以恢复未保存/删除的文档。此外&#xff0c;您还可以使用Word文件恢…

程序员如何搞副业?——程序员的副业建议

目录 前言 一、个人项目开发 二、在线教育和培训 三、技术博客和内容创作 总结 前言 程序员不仅拥有将抽象概念转化为实际应用的能力&#xff0c;还通常具备强大的逻辑思维和问题解决能力。然而&#xff0c;许多程序员并不满足于仅仅在一家公司工作&#xff0c;他们渴望通…

ES6: promise对象与回调地狱

ES6&#xff1a; promise对象与回调地狱 一、回调地狱二、Promise概述三、Promise的组成四、用函数封装Promise读取文件操作 一、回调地狱 在js中大量使用回调函数进行异步操作&#xff0c;而异步操作什么时候返回结果是不可控的&#xff0c;所以希望一段程序按我们制定的顺序执…

自定义注解(一)——统一请求拦截

文章目录 一、为什么会用到自定义注解二、关键参数说明三、应用场景示例&#xff1a;统一token认证1. 背景2. 自定义Token注解3. AOP上定义切面方法4. 方法上应用5. 总结 一、为什么会用到自定义注解 自定义注解可以帮助我们更好地组织和管理代码&#xff0c;提高代码的可读性和…

IIS服务器更换即将过期的SSL证书

公司IIS服务器证书快要过期&#xff0c;替换证书的步骤&#xff1a; Winr输入mstsc命令&#xff0c;显示远程登录&#xff1b;输入服务器IP以及密码&#xff0c;进行远程登陆登陆IIS服务器&#xff0c;winr输入inetmgr命令显示IIS操控器&#xff1b;选择服务器证书--点击服务器…

Springboot引入swagger

讲在前面&#xff1a;在spring引入swagger时&#xff0c;由于使用的JDK、Spring、swagger 的版本不匹配&#xff0c;导致启动报错&#xff0c;一直存在版本依赖问题。所以在此声明清楚使用版本。JDK 1.8、Spring boot 2.6.13、 Swagger 2.9.2。 引入maven依赖 <dependency&…

神经射频脉冲术,破解疼痛之锁

一位十余年糖尿病病史的患者&#xff0c;右足开始出现疼痛和麻木的症状三个多月&#xff0c;给他的生活带来了极大的困扰。他曾在多家医院就诊&#xff0c;但治疗效果并不理想。直到他走进了北京精诚博爱医院&#xff0c;这里为他带来了希望和转机。 经过详细的检查&#xff0c…

VR紧急情况模拟|V R体验中心加盟|元宇宙文旅

通过VR技术实现紧急情况模拟&#xff0c;提升安全应急能力&#xff01; 简介&#xff1a;面对突发紧急情况&#xff0c;如火灾、地震、交通事故等&#xff0c;正确的反应和应对能够有效减少伤害和损失。为了提高人们在紧急情况下的应急能力&#xff0c;我们借助先进的虚拟现实…

Unittest单元测试框架之unittest_执行用例的详细信息

unittest_执行用例的详细信息 用unittest.main()执行测试集 这里的verbosity是一个选项,表示测试结果的信息复杂度&#xff0c;有三个值&#xff1a; 0 (静默模式): 你只能获得总的测试用例数和总的结果 比如 总共100个 失败20 成功801 (默认模式): 非常类似静默模式 只是在…

蓝桥杯 每天2题 day6

碎碎念&#xff1a;哇咔咔 要不是中间缺勤一天就圆满day7了&#xff01;最后一晚上&#xff01;写题复习哇咔咔 唉&#xff0c;睡了一觉就看不下去了&#xff0c;&#xff0c;&#xff0c;看看之前的笔记洗洗睡觉&#xff0c;&#xff0c;&#xff0c; 记得打印准考证带好东西…

快速掌握数据层内置持久化方案-jdbcTemplateSpringBoot内置数据库

环境准备 导入jdbc的起步依赖&#xff0c;mybatis的依赖中就存在jdbc的起步依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>在配置文件中配置datasourse的…

使用腾讯云服务器如何搭建网站?新手建站教程

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网txyfwq.com整理使用腾讯云服务器建站教程&#xff0c;…

RPA实战演练UiBot6.0新食堂一楼问卷星(类似于之前的网页表单提交)

要使用RPA&#xff08;Robotic Process Automation&#xff0c;机器人流程自动化&#xff09;帮助新食堂进行调查问卷&#xff0c;我们可以结合UiBot 6.0来实施具体的计划。以下是一个大致的实战演练计划&#xff1a; 一、目标与需求分析 明确调查目标&#xff1a;了解新食堂…

ThignsBoard通过服务端订阅共享属性

MQTT基础 客户端 MQTT连接 通过服务端订阅属性 案例 1、首先需要创建整个设备的信息&#xff0c;并复制访问令牌 ​​2、通过工具MQTTX连接上对应的Topic 3、测试链接是否成功 4、在MQTT上订阅对应的Topic 5、在客户端添加共享属性信息 6、查看整个设备的遥测数据 M…

AD7982BRMZRL7 二进制 500kSPS 模数转换芯片 ADI

AD7982BRMZRL7是一款由Analog Devices&#xff08;亚德诺&#xff09;公司生产的18位逐次逼近型模数转换器&#xff08;ADC&#xff09;。它主要用于将模拟信号转换为数字信号&#xff0c;适用于数据采集系统、嵌入式系统、工业控制和医疗设备等领域。 AD7982BRMZRL7的主要功能…