高级IO之epoll模型

news2025/2/26 2:30:52

一、epoll模型介绍

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,用于监视一个或多个文件描述符,以查看它们是否可以进行读取、写入或异常处理。它能够显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。


二、epoll函数参数介绍

epoll有如下函数:

  1. epoll_create函数:用于生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。在某些内核版本中,该参数用于初始化哈希表的大小。
  2. epoll_ctl函数:用于控制某个文件描述符上的事件,可以注册事件、修改事件、删除事件。
  • 参数:epfd(由epoll_create生成的epoll专用的文件描述符);op(要进行的操作,例如注册事件,可能的取值有EPOLL_CTL_ADD(注册)、EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除));fd(关联的文件描述符);event(指向epoll_event的指针)。
  1. epoll_wait函数:用于轮询I/O事件的发生。
  • 参数:epfd(由epoll_create生成的epoll专用的文件描述符);events(指向epoll_event的指针);maxevents(最多可以返回的事件数量);timeout(等待的时间,如果设置为-1则无限等待)。

另外,epoll除了提供select/poll那种IO事件的水平触发外,还提供了边缘触发,这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。


三、epoll模型底层和运行过程

epoll模型在内核中的运行主要涉及以下步骤:

  1. 创建epoll模型:通过调用epoll_create函数创建一个文件描述符,这个文件描述符对应一个红黑树和一个就绪队列。红黑树用于存放需要关注的文件描述符及其对应的事件,而就绪队列则用于存放已经就绪的文件描述符及其事件。
  2. 添加文件描述符及监听事件:通过epoll_ctl函数向红黑树中添加文件描述符和监听事件。这些信息包括文件描述符、需要监听的事件以及二叉树的其他必要内容。此外,还需要建立回调策略,以便在有数据到达时通知内核处理。
  3. 查找并传递就绪的文件描述符:当某个文件描述符的事件就绪时,内核会将其加入到就绪队列中。用户态的程序可以通过调用epoll_wait函数来等待这些就绪的文件描述符。epoll_wait函数会观察就绪链表,并将就绪的文件描述符返回给用户态程序。这些文件描述符是通过内存映射的方式传递的,减少了不必要的拷贝操作。
  4. 重复监听的处理:当需要重复监听某个文件描述符时,只需再次调用epoll_ctl函数添加该文件描述符和监听事件。由于红黑树和就绪队列已经存在,所以无需重新构建数据结构,直接沿用即可。

总之,epoll模型在内核中的运行涉及到创建模型、添加文件描述符及监听事件、查找并传递就绪的文件描述符以及重复监听的处理等步骤。通过这些步骤,epoll模型能够高效地处理大量的文件描述符和事件,提供更好的系统性能和响应能力。


四、两种通信机制

ET和LT是两种不同的通信机制,它们在通信过程中有不同的特点和行为。

ET(Event Trigger)是一种事件触发的通信机制。在这种机制中,发送方在发送数据时不需要等待接收方的回应,一旦发送完成,发送方就会继续执行后续的操作。当接收方收到数据后,会触发相应的事件进行处理。这种机制的特点是发送方不会被阻塞,可以同时处理多个数据发送和执行其他任务。因此,ET通信机制具有高效性和并发性。这种机制要求程序员尽快将数据取走!

LT(Level Trigger)是一种电平触发的通信机制。在这种机制中,发送方会持续发送数据,直到接收方处理完之前的数据或者发送方主动停止发送。在这个过程中,发送方会被阻塞,直到接收方处理完之前的数据。LT通信机制的特点是简单易用,但是在处理大量数据时可能会导致发送方被长时间阻塞,影响程序的效率和响应能力。

在ET模式下,我如何直到我读的底层数据读完了?

答:循环读取,直到底层拒绝了我的读取请求!也就是没有数据了,这样的话就要求我们必须使用非阻塞fd来进行IO,这样在我们最后一次读不到数据的时候就可以不阻塞等待!


五、代码实现epoll服务端模式


#pragma once
#include <iostream>

#include "log.hpp"
#include "sock.hpp"
#include <functional>
#include <sys/epoll.h>
#define NEW_NUM 1024
static const uint16_t defaultport = 8080;
static const int size = 10;
static const int defaultvalue = 666;
namespace epoll_ns
{
    using func_t = std::function<std::string(const std::string &)>;
    class epollServer
    {
    public:
        epollServer(func_t cb, const uint16_t port = defaultport) : _port(port), _listensock(defaultvalue), _revs(nullptr), _f(cb)
        {
        }
        void Accepter()
        {
            logMessage(DEBUG, "Accepter in");

            std::string clientip;
            uint16_t clientport;
            int fd = sock::Accept(_listensock, &clientip, &clientport);
            if (fd < 0)
            {
                logMessage(WARNING, "accept error");
                return;
            }
            struct epoll_event ev;
            ev.events = EPOLLIN ;
            ev.data.fd = fd;
            epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
            logMessage(DEBUG, "Accepter out");
        }
        void Recver(int fd)
        {
            logMessage(DEBUG, "Recver in");
            char buffer[NEW_NUM];
            while (true)
            {
                //循环读取这样保证数据全部读取走
                int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
                if (n > 0)
                {
                    buffer[n-1] = 0;
                    logMessage(DEBUG, "client# %s", buffer);
                    string request = buffer;                    
                    string resp = _f(request) + '\n';           //伪处理回调机制
                    write(fd, resp.c_str(), resp.size());
                }
                else if (n == 0)
                {
                    // 一定要记住关闭!
                    epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                    close(fd);
                    logMessage(NORMAL, "client quit!");
                    return;
                }
                else
                {
                    epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                    close(fd);
                    logMessage(WARNING, "recv error!");
                    return;
                }
            }
                logMessage(DEBUG, "Recver out");
        }
        void handler(int readyNum)
        {

            logMessage(DEBUG, "handler events in");
            for (int i = 0; i < readyNum; i++)
            {
                uint32_t events = _revs[i].events;
                int sock = _revs[i].data.fd;
                if (sock == _listensock && (events & EPOLLIN))
                {
                    // accept就绪!
                    Accepter();
                }
                else if (events & EPOLLIN)
                {
                    Recver(sock);
                }
            }

            logMessage(DEBUG, "handler events out");
        }

        void init()
        {
            // 1.创建->绑定->监听 套接字
            _listensock = sock::GetSocket(_port);
            sock::Bind(_listensock, _port);
            sock::Listen(_listensock);
            // 2.创建epoll模型 通知机制-> 1.LT(level trigglered) 水平触发 一直通知   2.ET(edge trigglered) 边缘触发 通知一次直到有新的数据到来
            _epfd = epoll_create(size);
            if (_epfd < 0)
            {
                logMessage(FALTAL, "epoll create error: %s", strerror(errno));
                exit(3);
            }
            // 3.添加listen套接字到epoll中
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = _listensock; // 作用:当事件就绪时,被重新获取时候,我没要知道是哪个fd就绪了!
            epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);

            // 4.申请就绪空间
            _revs = new struct epoll_event[size];
        }

        void start()
        {
            while (true)
            {
                int timeout = 5000;
                int n = epoll_wait(_epfd, _revs, size, timeout);    //等待五秒就通知一次
                switch (n)
                {
                case 0:         //没有任务就绪
                    logMessage(NORMAL, "time out....");
                    break;
                case -1:        //出现异常
                    logMessage(WARNING, "epoll_wait error code:%d, error string : %s", errno, strerror(errno));
                    break;
                default:       //有任务就绪
                    logMessage(NORMAL, "events get ready!");
                    handler(n);
                    break;
                }
            }
        }
        ~epollServer()
        {
            if (_listensock != defaultvalue)
                close(_listensock);
            if (_epfd != defaultvalue)
                close(_epfd);
            if (_revs)
                delete[] _revs;
        }

    private:
        uint16_t _port;              //设置的端口号
        int _listensock;             //监听套接字
        int _epfd;                   //epoll模型文件描述符
        struct epoll_event *_revs;  //关心的事件
        func_t _f;                  //回调处理函数
    };
}

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

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

相关文章

QT学习日记 | 初始QT

目录 一、创建QT文件 二、目录结构讲解 1、.pro文件 2、源文件与头文件 3、编译运行 4、界面文件 三、梦开始的地方&#xff08;Hello World&#xff01;&#xff09; 1、代码方式 2、拖拽方式 四、Qt中的“容器” 五、Qt的对象树机制 1、对象树的引入 2、对象树…

【学网攻】 第(13)节 -- 动态路由(OSPF)

系列文章目录 目录 系列文章目录 文章目录 前言 一、动态路由是什么&#xff1f; 二、实验 1.引入 实验拓扑图 实验配置 实验验证 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学…

C++设计模式介绍:优雅编程的艺术

物以类聚 人以群分 文章目录 简介为什么有设计模式&#xff1f; 设计模式七大原则单一职责原则&#xff08;Single Responsibility Principle - SRP&#xff09;开放封闭原则&#xff08;Open/Closed Principle - OCP&#xff09;里氏替换原则&#xff08;Liskov Substitution …

【RT-DETR有效改进】EfficientFormerV2移动设备优化的视觉网络(附对比试验效果图)

前言 大家好&#xff0c;我是Snu77&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持Re…

DjangoURL调度器(一)

一、介绍 当一个用户请求 Django 站点的一个页面&#xff0c;下面是 Django 系统决定执行哪个 Python 代码使用的算法&#xff1a; Django确定要使用的根URLconf模块&#xff0c;一般是在settings中的ROOT_URLCONF设置的值&#xff0c;但是如果传入 HttpRequest 对象具有一个ur…

Unity 开发过程中如何优化内存

在开发Unity游戏时&#xff0c;优化内存使用是非常重要的。这不仅可以提高游戏性能&#xff0c;还能保证游戏在各种设备上都能顺利运行。以下是一些关于如何在Unity中优化内存使用的建议&#xff1a; 1. 了解并监控您的内存使用情况&#xff1a;您可以使用Unity的Profiler工具…

《合成孔径雷达成像算法与实现》Figure5.17

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

使用 Ant Design Pro 初始化前端项目

一、使用 pro-cli 来快速的初始化脚手架 1. 打开终端&#xff0c;输入命令 # 使用 npm npm i ant-design/pro-cli -g # create 后面加要初始化的项目名称 pro create leapi-frontend 2. 报错 PS D:\code> pro create leapi-frontend pro : 无法加载文件 D:\tools\nodejs…

AI 电子书链接分享

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

短剧小程序分销系统开发:创新与机遇的融合

一、引言 随着移动互联网的快速发展&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;正逐渐成为人们生活中的一部分。短剧小程序分销系统的开发&#xff0c;不仅为短剧的传播提供了新的渠道&#xff0c;同时也为相关产业带来了新的商业机会。本文将探讨短剧小程序分销系统…

SpringBoot对Bean的管理

Bean扫描 Spring中使用标签扫描或者注解 Springboot中没有使用标签或者注解它是怎么扫描的我的controlelr&#xff0c;service等等 核心在于springboot启动类中的SpringBootApplication注解 此注解其实是一个组合注解 它组合了一个ComponentScan注解&#xff0c;相当于在启…

小游戏选型(二):第三方社交小游戏厂家对比,即构/声网/融云/云信等

前言&#xff1a; 上一篇文章我们主要介绍社交游戏化趋势&#xff0c;并分析了直播平台面临的买量贵、变现难等问题&#xff0c;探讨了小游戏作为新的运营变现玩法的优势。同时还列举了各大直播平台TOP5的小游戏。今天我们继续介绍小游戏系列内容&#xff0c;本文是该系列的第…

【Docker】快速入门手册

目录 1.概述 1.1.安装 1.2.阿里云镜像加速 1.3.运行原理 2.常用操作 2.1.帮助命令 2.2.镜像操作 2.3.容器操作 2.3.1创建、启动 2.3.2.退出、停止 2.3.3.进入交互式界面 2.3.4.守护式容器交互 2.3.5.查看 2.3.6.删除 2.3.7.拷贝 3.容器数据卷 3.1.概述 3.2.使…

阅读记录:RNNLOGIC: LEARNING LOGIC RULES FOR REASON-ING ON KNOWLEDGE GRAPHS

一、介绍 本文研究知识图谱推理的学习逻辑规则。 逻辑规则在用于预测时提供可解释的解释&#xff0c;并且能够推广到其他任务。现有方法要么面临在大搜索空间中搜索的问题&#xff08;例如神经逻辑编程&#xff09;&#xff0c;要么由于奖励稀疏而导致优化无效&#xff08;例…

Vue-Cli3 - 从安装 nodejs 配置环境 ~ 搭建 cli 脚手架项目全过程

目录 前言提示 一、安装 & 配置 nodejs 1.1、安装 nodejs 1.2、配置必要目录 1.3、配置环境变量 1.4、测试 安装&配置 是否成功 1.5、安装淘宝镜像 1.5、cnpm 安装&#xff08;推荐安装&#xff09; 二、vue-cli3 创建项目 2.1、vue-cli2 和 vue-cli3 主要区…

玩转WEB接口之三 【HTTPS证书申请】

文章目录 一、概述主要流程二、域名注册1. 购买域名2. 购买服务器3. 域名备案4. 域名解析 三、证书申请1. 申请途径2. 阿里云3个月免费SSL申请3. freessl 1年免费SSL申请 四、证书验证1. springboot 代码验证1.) 证书转换2.) 验证结果3.) 源码传送 2. nginx验证 一、概述 HTTP…

行测-资料:2. 一般增长率、增长量

1、一般增长率 1.1 百分数和百分点 50%&#xff0c;20% 1.2 增长率和倍数 1.5&#xff1b;50 1.3 成数和翻番 1.4 增幅&#xff0c;降幅&#xff0c;变化幅度 A&#xff0c;A&#xff0c;D B&#xff0c;高于全国增速 2.3 个百分点&#xff0c;21.8 - 2.3 19.5。 5%&#xff0…

数字图像处理(实践篇)三十四 OpenCV-Python绘制椭圆

目录 一 涉及的函数 二 实践 一 涉及的函数 cv2.ellipse(img,center,axes,angle,start_angle,end_angle,color,thickness) 参数: ①<

精品基于Uniapp+ssm宠物时光管理系统App

《[含文档PPT源码等]精品基于Uniappssm宠物时光管理系统App》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 后台框架&#xff1a;ssm 安卓框架&#xff1a…

【目标跟踪】多相机环视跟踪

文章目录 一、前言二、流程图三、实现原理3.1、初始化3.2、输入3.3、初始航迹3.4、航迹预测3.5、航迹匹配3.6、输出结果 四、c 代码五、总结 一、前言 多相机目标跟踪主要是为了实现 360 度跟踪。单相机检测存在左右后的盲区视野。在智能驾驶领域&#xff0c;要想靠相机实现无…