【Linux】:epoll

高级IO

  • 一.各种接口
  • 二.工作模式

按照man手册的说法: 是为处理大批量句柄而作了改进的poll.

一.各种接口

快速认识接口:

在这里插入图片描述

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

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

epoll_ctl:

它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型.
第一个参数是epoll_create()的返回值(epoll的句柄).
第二个参数表示动作,用三个宏来表示.:
第三个参数是需要监听的fd.
第四个参数是告诉内核需要监听什么事

第二个参数取值:

EPOLL_CTL_ADD :注册新的fd到epfd中;
EPOLL_CTL_MOD :修改已经注册的fd的监听事件;
EPOLL_CTL_DEL :从epfd中删除一个fd;

epoll模型:

epoll模型是一个结构体,它被同一管理,然后接入到struct file里,所以epoll的返回值也是一个文件描述符。

创建epoll模型就是创建红黑树,就绪队列和底层回调函数。网卡接收数据,触发硬件中断,操作系统将数据拷贝到网卡驱动里,再经过回调函数(将数据交付给tcp的接收缓冲区)查找红黑树节点是否有关心该数据,如果有,就创建新节点插入到就绪队列里,用户直接就可以从就绪队列里拿数据了。

在这里插入图片描述

所以epollctl本质上就是在修改这棵红黑树。epoll_wait就是在关联就绪队列。通过弹出就绪队列的节点就有效避免了select和poll的遍历问题。

epoll优势

  1. 接口使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效. 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开
  2. 数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)
  3. 事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符结构加入到就绪队列中,
  4. epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度O(1). 即使文件描述符数目很多, 效率也不会受到影响.
  5. 没有数量限制: 文件描述符数目无上限

二.工作模式

epoll有2种工作方式-水平触发(LT)和边缘触发(ET)

epolI默认模式:LT模式。事件到来,但是上层不处理,高电平,一直有效
ET:数据或者连接,从无到有,从有到多,变化的时候,才会通知我们一次

LT是 epoll 的默认行为. 使用 ET 能够减少 epoll 触发的次数. 但是代价就是强逼着程序猿一次响应就绪过程中就把所有的数据都处理完.因为它只有一次提示。
相当于一个文件描述符就绪之后, 不会反复被提示就绪, 看起来就比 LT 更高效一些. 但是在 LT 情况下如果也能做到每次就绪的文件描述符都立刻处理, 不让这个就绪被重复提示的话, 其实性能也是一样的.另一方面, ET 的代码复杂程度更高了

通知本质上就是添加就绪队列节点。如果是ET模式,就是只添加一次;如果是LT就是每次都添加。可以使用EPOLLET进行设置。

简单使用例子:

1.epoller封装

#pragma once

#include "nocopy.hpp"
#include "Log.hpp"
#include <cerrno>
#include <cstring>
#include <sys/epoll.h>

class Epoller : public nocopy
{
    static const int size = 128;

public:
    Epoller()
    {
        _epfd = epoll_create(size);
        if (_epfd == -1)
        {
            lg(Error, "epoll_create error: %s", strerror(errno));
        }
        else
        {
            lg(Info, "epoll_create success: %d", _epfd);
        }
    }
    int EpollerWait(struct epoll_event revents[], int num)
    {
        int n = epoll_wait(_epfd, revents, num, /*_timeout 0*/ -1);
        return n;
    }
    int EpllerUpdate(int oper, int sock, uint32_t event)
    {
        int n = 0;
        if (oper == EPOLL_CTL_DEL)
        {
            n = epoll_ctl(_epfd, oper, sock, nullptr);
            if (n != 0)
            {
                lg(Error, "epoll_ctl delete error!");
            }
        }
        else
        {
            // EPOLL_CTL_MOD || EPOLL_CTL_ADD
            struct epoll_event ev;
            ev.events = event;
            ev.data.fd = sock; // 目前,方便我们后期得知,是哪一个fd就绪了!

            n = epoll_ctl(_epfd, oper, sock, &ev);
            if (n != 0)
            {
                lg(Error, "epoll_ctl error!");
            }
        }
        return n;
    }
    ~Epoller()
    {
        if (_epfd >= 0)
            close(_epfd);
    }

private:
    int _epfd;
    int _timeout{3000};
};

2.epoller使用

#pragma once

#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "nocopy.hpp"

uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);

class EpollServer : public nocopy
{
    static const int num = 64;

public:
    EpollServer(uint16_t port)
        : _port(port),
          _listsocket_ptr(new Sock()),
          _epoller_ptr(new Epoller())
    {
    }
    void Init()
    {
        _listsocket_ptr->Socket();
        _listsocket_ptr->Bind(_port);
        _listsocket_ptr->Listen();

        lg(Info, "create listen socket success: %d\n", _listsocket_ptr->Fd());
    }
    void Accepter()
    {
        // 获取了一个新连接
        std::string clientip;
        uint16_t clientport;
        int sock = _listsocket_ptr->Accept(&clientip, &clientport);
        if (sock > 0)
        {
            // 我们能直接读取吗?不能
            _epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, sock, EVENT_IN);
            lg(Info, "get a new link, client info@ %s:%d", clientip.c_str(), clientport);
        }
    }
    // for test
    void Recver(int fd)
    {
        // demo
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "get a messge: " << buffer << std::endl;
            // wrirte
            std::string echo_str = "server echo $ ";
            echo_str += buffer;
            write(fd, echo_str.c_str(), echo_str.size());
        }
        else if (n == 0)
        {
            lg(Info, "client quit, me too, close fd is : %d", fd);
            //细节3
            _epoller_ptr->EpllerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            lg(Warning, "recv error: fd is : %d", fd);
            _epoller_ptr->EpllerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(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 == _listsocket_ptr->Fd())
                {
                    Accepter();
                }
                else
                {
                    // 其他fd上面的普通读取事件就绪
                    Recver(fd);
                }
            }
            else if (events & EVENT_OUT)
            {
            }
            else
            {
            }
        }
    }
    void Start()
    {
        // 将listensock添加到epoll中 -> listensock和他关心的事件,添加到内核epoll模型中rb_tree.
        _epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, _listsocket_ptr->Fd(), EVENT_IN);
        struct epoll_event revs[num];
        for (;;)
        {
            int n = _epoller_ptr->EpollerWait(revs, num);
            if (n > 0)
            {
                // 有事件就绪
                lg(Debug, "event happened, fd is : %d", revs[0].data.fd);
                Dispatcher(revs, n);
            }
            else if (n == 0)
            {
                lg(Info, "time out ...");
            }
            else
            {
                lg(Error, "epll wait error");
            }
        }
    }
    ~EpollServer()
    {
        _listsocket_ptr->Close();
    }

private:
    std::shared_ptr<Sock> _listsocket_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;
    uint16_t _port;
};

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

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

相关文章

智慧灯杆综合管理平台建设思路及系统方案设计

一、建设思路 智慧灯杆综合管理平台的建设旨在实现城市基础设施的智能化、信息化和高效化。在构建这一平台时&#xff0c;我们首先要明确其核心理念&#xff1a;以灯杆为载体&#xff0c;整合各类城市服务资源&#xff0c;通过先进的物联网技术和大数据分析&#xff0c;为城市…

欧美助听器市场热门品牌盘点,国产爱可声备受青睐

近年来&#xff0c;随着中国技术的不断进步和品质的提升&#xff0c;国产助听器品牌爱可声在欧美市场备受瞩目。在欧美国家助听器市场&#xff0c;有许多热门品牌分别为&#xff1a;峰力、斯达克、瑞声达、爱可声等。这些品牌凭借其悠久的历史、先进的技术和高品质的产品&#…

Python3:强大的编程语言及其广泛应用

点击下载《Python3&#xff1a;强大的编程语言及其广泛应用》 1. 前言 Python3作为一种强大的编程语言&#xff0c;凭借其简洁易读、功能丰富以及强大的扩展性&#xff0c;已经成为编程界的热门选择。本文将详细介绍Python3的主要特性、应用领域以及它在实际项目中的优势&…

胖东来的商业启示!2024创业赛道!2024创业新项目新商机!2024创业风口!2024普通人的创业机会!2024创业赚钱项目!

在中国零售业&#xff0c;胖东来是“神”一般的存在&#xff0c;不讲业绩&#xff0c;只讲幸福&#xff0c;却造就了全国最具人效和坪效的零售门店&#xff0c;都说胖东来成功的核心秘诀是“把人当人”。 当然胖东来的成功可不是偶然&#xff0c;它背后是于东来多年来的坚持与努…

提高在线考试成绩的备考技巧与建议

参加在线考试已经成为现代学习的一部分&#xff0c;它的便利性和灵活性为学生提供了更多的学习机会。然而&#xff0c;为了在在线考试系统中取得好成绩&#xff0c;备考技巧是非常重要的。 设置明确的学习目标 在备考在线考试之前&#xff0c;为自己设定明确的学习目标是非常重…

通义灵码-IDEA的使用教程

通义灵码-IDEA的使用教程 1、通义灵码是什么&#xff1f; 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研发智能问答、异常报错排查等能力&#…

【svgwrite 库简介,附代码】如何将 .ttf 字体文件转化为 .svg 矢量图形?

当涉及到字体和矢量图形时&#xff0c;.ttf 和 .svg 是两种不同的文件格式&#xff1a; .ttf (TrueType Font)&#xff1a; .ttf 文件是一种常见的字体文件格式&#xff0c;用于存储 TrueType 字体。TrueType 是一种字体轮廓的标准&#xff0c;它使用贝塞尔曲线描述字形&…

linux中git的使用

为什么要有git git相当于一个仓库可以让我们更好的去管理我们的代码&#xff0c;实现版本的控制&#xff0c;上传到云端仓库。有了git,就可以实现多人同时开发一个项目&#xff08;每个负责一部分代码&#xff0c;最后都上传到同一个仓库&#xff09;。 git github/gitee 的区…

羊大师分享,羊奶夏日里的养生佳品

羊大师分享&#xff0c;羊奶夏日里的养生佳品 羊奶&#xff0c;作为夏日里的养生佳品&#xff0c;确实拥有诸多益处。在炎炎夏日&#xff0c;人体面临着高温、湿度的双重考验&#xff0c;身体容易出现各种不适&#xff0c;而羊奶正好可以为我们提供多方面的滋养与保护。 羊奶…

UTONMOS:用区块链技术拓展商业边界在哪里?

引言 大约从 2021 年Web 3 这个新概念开始受到风险基金和科技圈的普遍关注。但如果你对过去几年区块链的发展历史足够了解&#xff0c;就应该已经意识到现在的 Web 3 并不是什么新技术&#xff0c;甚至不是旧技术的进步&#xff0c;它只是一个基于区块链技术的宏大构想。 我是…

光伏储能控制系统的功能策略

一、控制策略 1、功率控制策略 光伏阵列的输出功率受光照和温度影响&#xff0c;最大功率点是转换太阳能为电能的最高效点。MPPT控制器根据实时参数调整光伏阵列工作点&#xff0c;确保其始终处于最大功率输出状态&#xff0c;提高能量转换效率&#xff0c;增加发电量&#x…

产品经理好干吗?

赚钱的工作都不轻松吧&#xff0c;除非是家里资源好的。 产品经理的薪资还是可观的 &#xff0c;小白如何成为产品经理&#xff1f; 首先要有经验&#xff0c;有理论知识&#xff0c;对想从事产品经理相关岗位的人来说&#xff0c;这基本是个必考的证书了&#xff0c;感兴趣的…

【每天一个linux小知识】nslookup

往期文章 tailf 和 tail -f 目录 往期文章什么是 nslookup示例参考 什么是 nslookup nslookup全称 name server lookup nslookup 是一款常用的网络工具&#xff0c;用于查询 DNS 的记录。通过 nslookup&#xff0c;用户可以查找特定主机的 IP 地址、查询域名的 MX 记录&…

Seatunnel-web build:prod 报错 jsx问题

问题1 JSX element implicitly has type any because no interface JSX.IntrinsicElements exists. 问题剖析 在使用typescript的时候&#xff0c;在vue或react、node中报以上错误&#xff0c;是JSX 元素隐式具有类型 "any"&#xff0c;因为不存在全局类型 "J…

three.js 3D Banner实战

需求场景 1、加载glb模型 2、设置背景 3、让第一个模型转动&#xff0c;并调整模型的转动速度 4、设置模型的宽度。 遇到的问题 1、为什么加载的模型不能转动&#xff1f; 加载的glb模型需要加入到goup里面才能转动。 代码实施 <script setup> import * as THREE from…

UTC和北京时间

influxdb 的时间为UTC和北京时间相差8小时&#xff0c;需要经常转化&#xff0c;所以有下面的2个常用时间 public static void main(String[] args) {//北京时间当天0点ZonedDateTime dateTime ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));ZonedDateTime beijin…

【zabbix7】新版本尝鲜之connector

zabbix历史版本中&#xff0c;会使用python脚本&#xff0c;把zabbix的告警发送到kafka进行二次处理&#xff0c;或者使用filebeat把zabbix的Export的njson指标数据发送到kafka进行二次处理&#xff0c;然而在zabbix7中新增了新功能connector简化了操作并且可以根据tag进行区分…

微信订阅号绑定Coze(扣子),使用上GPT4

之前试过国外的Coze可以绑定一些通讯工具&#xff0c;从而可以使用相当于chatgpt4的功能&#xff0c;但对于我们来说&#xff0c;使用不太方便。 后来头条又有了国内版的Coze(扣子)&#xff0c;发现可以绑定微信&#xff0c;但那时只有微信服务号可以使用&#xff0c;个人订阅…

【嵌入式】“野指针”和“悬空指针”的奇淫拙劣

【嵌入式】“野指针”和“悬空指针”的奇淫拙劣 1. 前言1.1 授权须知 2. 野指针和悬空指针3.举例说明3.1 示例一&#xff1a;free 之后&#xff0c;没有让指针指向NULL3.1.1 代码解析3.1.2 运行代码的结果3.1.3 程序崩溃在哪&#xff1f; 3.2 悬空指针–释放后使用攻击 1. 前言…

【Linux】LInux下的进程状态

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…