重写muduo之EPollPoller

1、EPollPoller.h

EPollPoller的主要实现:作为poller的派生类,把基类给派生类保留的这些纯虚函数的接口实现出来。

override表示在派生类里面,这些方法是覆盖方法。必须由编译器来保证在基类里面一定有这些函数的接口的声明。在派生类要重写他们。
给EPollPoller的析构函数写override,就是让编译器给你检查基类的析构一定是虚函数。
底层是vector,放EventList,可以动态地扩容。
成员变量的epollfd要通过epoll_create来创建,映射的就是epoll底层的文件系统

#pragma once
#include "Timestamp.h"
#include "Poller.h"

#include <vector>
#include <sys/epoll.h>

/**
 * epoll的使用
 * epoll_create
 * epoll_ctl  add/mod/del
 * epoll_wait
*/
class EPollPoller:public Poller
{
public:
    EPollPoller(EventLoop* loop);//epoll_create
    ~EPollPoller()override;

    //重写基类Poller的抽象方法
    Timestamp poll(int timeoutMs,ChannelList* activeChannels)override;//epoll_wait
    void updateChannel(Channel* channel)override;//epoll_ctl
    void removeChannel(Channel* channel)override;//epoll_ctl
private:
    static const int kInitEventListSize=16;//给vector<epoll_event>初始化的长度
    using EventList=std::vector<epoll_event>;

    //填写活跃的连接
    void fillActiveChannels(int numEvents,ChannelList* activeChannels)const;

    //更新channel通道
    void update(int operation,Channel* channel);

    int epollfd_;
    EventList events_;
};

2、EPollPoller.cc

epoll_wait:第2个参数epoll_event 是最终发生事件的fd,返回值是发生事件fd的数量。

epoll_create的参数size在Liunx内核2.6.8以后没有意义了,但是必须是大于0的数


epoll_create1,flags=0时和epoll_create一样,提供了行为选项EPOLL_CLOEXEC

当我们去使用epoll_create1的时候,创建的epollfd,然后在当前线程里面再去fork创建一个子进程,然后用exec替换子进程的时候,在子进程里面就把父进程设置成标志的fd,资源就都给关闭了。

结构体的fd就是epoll要监听的事件,ptr指向fd对应的channel,channel包含fd以及感兴趣的事件

#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"

#include <errno.h>
#include <unistd.h>
#include <string.h>

// channel还没有被添加到Poller中
const int kNew = -1; // channel的成员index_=-1
// channel已经添加到Poller中
const int kAdded = 1;
// channel从Poller中删除
const int kDeleted = 2;

EPollPoller::EPollPoller(EventLoop *loop)
    : Poller(loop), epollfd_(::epoll_create1(EPOLL_CLOEXEC)), events_(kInitEventListSize) // vector<epoll_event> 默认大小16
{
    if (epollfd_ < 0)
    {
        LOG_FATAL("epoll_create error:%d \n", errno);
    }
}

EPollPoller::~EPollPoller()
{
    ::close(epollfd_);
}

//epoll_wait 
//eventloop会创建一个channellist,并把创建好的channellist的地址传给poll
//poll通过epoll_wait监听到哪些fd发生了事件,把真真正正发生事件的channel通过形参发送到eventloop提供的实参里面 
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
    // 实际上应该用LOG_DEBUG输出日志更为合理,可以设置开启或者不开启 因为LOG_INFO是每次都要输出的,会影响epoll的效率
    LOG_INFO("func=%s => fd total count:%lu\n", __FUNCTION__, channels_.size());

    //events_.begin()返回首元素的迭代器(数组),也就是首元素的地址,是面向对象的,要解引用,就是首元素的值,然后取地址 
    //就是vector底层数组的起始地址   static_cast类型安全的转换   timeoutMs超时时间 
    int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs); 

    //全局的变量errno,库里的,poll可能在多个线程eventloop被调用 ,所以用局部变量存起来 
    int saveErrno = errno;  // 在loop开始时,保存当前loop的errno,防止中间操作发生错误对全局的errno进行改写,那在日志打印的时候就获取不到当前loop的errno了
    Timestamp now(Timestamp::now());

    if (numEvents > 0)//表示有已经发生相应事件的个数 
    {
        LOG_INFO("%d events happened \n", numEvents);
        fillActiveChannels(numEvents, activeChannels);

        // 如果返回的numEvents和实际vector中的events的长度是一样的,说明这一轮监听的所有的event都发生事件了,就要进行扩容了
        if (numEvents == events_.size())
        {
            events_.resize(events_.size() * 2);
        }
    }
    else if (numEvents == 0) // 没有事件发生,超时
    {
        LOG_DEBUG("%s timeout!\n", __FUNCTION__);
    }
    else // 发生错误
    {
        if (saveErrno != EINTR) // EINTR=>外部中断,不等于外部的中断 ,是由其他错误类型引起的 
        {
            errno = saveErrno; //适配 ,把errno重置成当前loop之前发生的错误的值 
            LOG_ERROR("EPollPoller::poll() err!");
        }
    }
    return now;
}

// channel update remove=>EventLoop updateChannel removeChannel=>Poller updateChannel removeChannel
/**
 *                   EventLoop   =>    poller.poll
 *          ChannelList        Poller
 *                             ChannelMap   <fd,Channel*>  (保存的是向poller注册过的channel)  epollfd
 */
void EPollPoller::updateChannel(Channel *channel)
{
    const int index = channel->index();
    LOG_INFO("func=%s=> fd=%d events=%d index=%d \n", __FUNCTION__, channel->fd(), channel->events(), index);

    if (index == kNew || index == kDeleted)//未添加或者已删除 
    {
        if (index == kNew)//未添加,键值对写入map中 
        {
            int fd = channel->fd();
            channels_[fd] = channel;
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel);//相当于调用epoll_ctl,添加1个channel到epoll中 
    }
    else // channel已经在poller上注册过了
    {
        int fd = channel->fd();
        if (channel->isNoneEvent()) // channel对任何事件都不感兴趣,不需要poller帮忙监听了 
        {
            update(EPOLL_CTL_DEL, channel);//删除已注册的channel的感兴趣的事件 
            channel->set_index(kDeleted);
        }
        else
        {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}
// 从poller中删除channel
void EPollPoller::removeChannel(Channel *channel)
{
    int fd = channel->fd();
    channels_.erase(fd);

    LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);

    int index = channel->index();
    if (index == kAdded)//如果已注册过 
    {
        update(EPOLL_CTL_DEL, channel);//通过epoll_ctl 删掉 
    }
    channel->set_index(kNew);//设置成未添加的状态 
}

// 填写活跃的连接
void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
{
    for(int i=0;i<numEvents;i++)
    {
        Channel* channel=static_cast<Channel*>(events_[i].data.ptr);
        channel->set_revents(events_[i].events);
        activeChannels->push_back(channel);//EventLoop就拿到了它的poller给它返回的所有发生事件的channel列表了
        //至于EventLoop拿到这些channel干什么事情,我们看 EventLoop的代码 
    }
}

// 更新channel通道  epoll_ctl add/mod/del
void EPollPoller::update(int operation, Channel *channel)
{
    epoll_event event;
    memset(&event, 0, sizeof event);

    int fd = channel->fd();

    event.events = channel->events();//返回的就是fd所感兴趣的事件 
    event.data.fd = fd;
    event.data.ptr = channel;//绑定的参数 

    if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)//把fd相关事件更改 
    {
        if (operation == EPOLL_CTL_DEL)
        {
            LOG_ERROR("epoll_ctl del error:%d\n", errno);
        }
        else
        {
            LOG_FATAL("epoll_ctl add/mod error:%d\n", errno); // add/mod如果失败了,是无法挽回的,所以LOG_FATAL会自动exit
        }
    }
}

channel要把自己注册到poller上,但channel无法与poller直接通信,channel调用的是EventLoop的updatechannel和removechannel,EventLoop的updatechannel和removechannel最终还是调用的EPollPoller,做的相当于是epoll_ctl,最后进行epoll_wait就是EPollPoller中的poll函数,使用vector数组存放发生的事件,如果返回值numEvents和vector数组长度一样,说明可能还有更多的事件没有处理,需要扩容,下一轮再来处理,因为muduo库采用的是LT模式,没有处理的事件会不断上报。

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

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

相关文章

标准IO学习

思维导图&#xff1a; 有如下结构体 struct Student{ char name[16]; int age; double math_score; double chinese_score; double english_score; double physics_score; double chemistry_score; double bio_score; }; 申请该结构体数组&#xff0c;容量为5&#xff0c;初始…

(数据分析方法)长期趋势分析

目录 一、定义 二、目的 三、方法 1、移动平均法 (1)、简单移动平均法 (2)、加权移动平均法 (3)、指数平滑法 2、最小二乘法 3、线性回归 1、数据预处理 2、观察数据分布建立假设模型 3、定义损失函数 4、批量梯度下降 5、优化 4、LSTM 时序分析 5、特征工程 一…

智能家居1 -- 实现语音模块

项目整体框架: 监听线程4&#xff1a; 1. 语音监听线程:用于监听语音指令&#xff0c; 当有语音指令过来后&#xff0c; 通过消息队列的方式给消息处理线程发送指令 2. 网络监听线程&#xff1a;用于监听网络指令&#xff0c;当有网络指令过来后&#xff0c; 通过消息队列的方…

X 推出 Stories 功能,由 Grok AI 生成新闻摘要

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

OCC笔记:图形可视化的实现方式

注&#xff1a;文中参看的occ的源码版本为7.4.0 1、实现思路概览 整体架构 主要有3大块&#xff1a;AIS&#xff08;Application Interactive Services &#xff0c;直译为&#xff1a;应用程序交互服务&#xff09;、Graphics&#xff08;图形&#xff09;、Geometry & T…

磐石云最版本使用教程

磐石云外呼系统是一款集成了呼叫平台、电话线路和话术系统的软件&#xff0c;旨在提高企业的电话营销效率。以下是磐石云外呼系统的基本操作步骤&#xff1a; 安装和配置&#xff1a; 首先需要在Linux操作系统上安装磐石云外呼系统&#xff0c;通常需要至少4核CPU、8GB内存以及…

MyCat安装

MyCat安装 官网下载地址打不开说明采用站点的方式进行下载基础包 &#xff1a;程序包&#xff1a; 配置原型库数据源root.user.json 配置文件说明&#xff08;默认配置&#xff09; Mycat启动授权启动mycat启动mycat查看mycat日志连接Mycat 官网下载地址打不开说明 官网可能受…

FreeBSD RISCV 在QEME中实践-网络配置

在前一篇文章中&#xff0c;我们一起进行了FreeBSD RISCV 在QEME中实践 现在&#xff0c;让我们配置好网络吧&#xff01; 先上结论&#xff1a;用默认配置启动即可&#xff0c;网络就加载好了&#xff0c;只是不能ping罢了。因为不能ping&#xff0c;以为网络没通&#xff0…

linux文件夹权限查看以及设置

1.linux给文件夹和子文件夹开权限 2.查询当前文件夹权限

java 泛型题目讲解

泛型的知识点 泛型仅存在于编译时期&#xff0c;编译期间JAVA将会使用Object类型代替泛型类型&#xff0c;在运行时期不存在泛型&#xff1b;且所有泛型实例共享一个泛型类 public class Main{public static void main(String[] args){ArrayList<String> list1new Arra…

ASP.NET网上图书订阅系统的设计

摘 要 网上图书订阅系统基于 Microsoft SQL Server 2000和ASP.NET平台&#xff0c;以C#为编程语言开发,实现了网上图书预订和借阅&#xff0c;订阅信息查询&#xff0c;图书和用户信息的修改&#xff0c;借阅排行和新到图书的查询等功能&#xff0c;这样不但可将管理员从繁重…

Linux基础之makefile/make

目录 一、背景 二、makefile和make的讲解 2.1 使用方法 2.2 伪目标文件 2.3 文件的属性以及属性的更新 2.4 makefile的自动推导 一、背景 这里会提及为什么要使用makefile和make&#xff0c;以及他们是什么和作用。 会不会写makefile&#xff0c;从一个侧面说明了一个人是…

宝兰德通过广东教育行业信创适配认证,拓展教育信创生态圈

近日&#xff0c;由宝兰德自主研发的多款中间件产品通过广东省教育行业信创适配中心的适配测试。测试表明&#xff0c;宝兰德四款中间件产品&#xff08;分布式缓存软件V3.0、应用服务器软件V9.5、消息中间件软件 V2.1、Web服务器软件V3.1&#xff09; 与当前主流国产操作系统统…

memory consistency

memory consistency model 定义了对于programmer和implementor来说&#xff0c;访问shared memory system的行为&#xff1b; 对于programmer而言&#xff0c;他知道期望值是什么&#xff0c; 知道会返回什么样的数据&#xff1b;&#xff1b; 对于implementro而言&#xff0c;…

数据结构——链表专题1

文章目录 一、移除链表元素二、反转链表三、合并两个有序链表四、链表的中间节点五、环形链表的约瑟夫问题六、分割链表 一、移除链表元素 原题链接&#xff1a;移除链表元素 一个解法是遍历原链表&#xff0c;将与val相等的结点抛弃&#xff0c;链接后一个结点 另一个解法是…

corefBERT论文阅读

CorefBERT是清华大学团队发表的&#xff0c;继SpanBERT之后另一针对共指消解的BERT模型。共指消解任务对于文本理解、智能问答等其他NLP子任务起到至关重要的作用。 为了提高语言模型的共指推理能力&#xff0c;一个简单的解决方案是使用有监督的共指解析数据在bert等模型进行…

论文笔记ColdDTA:利用数据增强和基于注意力的特征融合进行药物靶标结合亲和力预测

ColdDTA发表在Computers in Biology and Medicine 的一篇一区文章 突出 • 数据增强和基于注意力的特征融合用于药物靶点结合亲和力预测。 • 与其他方法相比&#xff0c;它在 Davis、KIBA 和 BindingDB 数据集上显示出竞争性能。 • 可视化模型权重可以获得可解释的见解。 …

Linux网络部分——DNS域名解析服务

目录 1. 域名结构 2. 系统根据域名查找IP地址的过程 3.DNS域名解析方式 4.DNS域名解析的工作原理【☆】 5.域名解析查询方式 6.搭建主从DNS域名服务器 ①初始化操作主服务器和从服务器&#xff0c;安装BIND软件 ②修改主服务器的主配置文件、区域配置文件、区域数…

【c1】数据类型,运算符/循环,数组/指针,结构体,main参数,static/extern,typedef

文章目录 1.数据类型&#xff1a;编译器&#xff08;compiler&#xff09;与解释器&#xff08;interpreter&#xff09;&#xff0c;中文里的汉字和标点符号是两个字节&#xff0c;不能算一个字符&#xff08;单引号&#xff09;2.运算符/循环&#xff1a;sizeof/size_t3.数组…

基于java的CRM客户关系管理系统的设计与实现(论文 + 源码 )

【免费】基于Java的CRM客户关系管理系统的设计和实现.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89273409 基于Java的CRM客户关系管理系统的设计与实现 摘 要 随着互联网的高速发展&#xff0c;市场经济的信息化&#xff0c;让企业之间的竞争变得&#xff0…
最新文章