重写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_waitvoid updateChannel(Channel* channel)override;//epoll_ctlvoid 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.hqwc.cn/news/670934.html

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

相关文章

数据结构十一:数组相关经典面试题

本篇博客详细介绍分析数组/顺序表常见的面试题&#xff0c;对于前面所学知识进行一个巩固&#xff0c;同时介绍一些力扣刷题中的一些概念&#xff1a;如&#xff1a;输出型参数等&#xff0c;在刷题中培养自己的编程思维&#xff0c;掌握常见的编程套路&#xff0c;形成题感&am…

Jupyter配置

修改Jupyter打开界面 &#xff08;1&#xff09;打开【Anaconda Prompt】&#xff0c;输入【jupyter notebook --generate-config】命令 从运行结果可知【jupyter_notebook_config.py】的位置 &#xff08;2&#xff09;使用【记事本】打开 找到# c.NotebookApp.notebook_…

【菜单下拉效果】基于jquery实现二级菜单下拉效果(附完整源码下载)

Js菜单下拉特效目录 &#x1f354;涉及知识&#x1f964;写在前面实现效果&#x1f367;一、涉及知识&#x1f333;二、具体实现2.1 搭建一级菜单2.2 搭建二级菜单项2.3 引入js文件2.4 构建CSS文件 &#x1f40b;三、源码获取&#x1f305; 作者寄语 &#x1f354;涉及知识 ht…

leetCode76. 最小覆盖子串

leetCode76. 最小覆盖子串 题目思路 代码 // 双指针 哈希表 // 这里cnt维护过程&#xff1a;先找到能够匹配T字符串的滑动窗口&#xff0c;然后这个cnt就固定了&#xff0c;因为i向前移动的同时&#xff0c;j也会维护着向前 // 就是当又出现能够满足T字符串的时候&#xff0…

【计算机网络原理】万字长文,持续更新...

文章目录&#x1f970; 计算机网络原理1.2 因特网概述1 网络、互联网&#xff08;互连网&#xff09;和因特网2 因特网发展的三个阶段ISP的概念基于ISP的三层结构的因特网 3 因特网的标准化工作4 因特网的组成 1.3 三种交换方式&#xff1a;电路交换、分组交换和报文交换电路交…

MySQL CRUD操作

前言&#x1f440;~ 上一章我们介绍了数据库的一些基础操作&#xff0c;关于如何去创建一个数据库&#xff0c;还有使用数据库&#xff0c;删 除数据库以及对表进行的一些基础操作&#xff0c;今天我们学习CRUD操作 俗称&#xff08;增删改查&#xff09; 如果各位对文章的内…

《QT实用小工具·五十五》带有标签、下划线的Material Design风格输入框

1、概述 源码放在文章末尾 该项目实现了一个带有标签动画、焦点动画、正确提示、错误警告的单行输入框控件。下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef LABELEDEDIT_H #define LABELEDEDIT_H#include <QObject> #include <QWidget>…

问题管理员的工作角色、职责和技能

问题管理就是识别、分析和解决反复出现的根本原因问题并永久修复它们。听起来很简单对吧&#xff0c;不幸的是&#xff0c;情况并非总是如此。对于组织来说&#xff0c;IT问题管理一直是一门棘手的 ITSM 学科。一个经常被忽视的关键因素是有效的问题 管理不仅仅是工具和流程。 …

2024.5.6

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口大小this->resize(1000,740);//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\Administrator\\Desktop\\pictrue\\Plants.png"));//设置窗口标题this-…

Codeforces Round 943 (Div. 3) A~E

A. Maximize? Problem - A - Codeforces 给定x求出使这个式子最大的y&#xff1a; 不用想复杂直接循环枚举即可。 #include<bits/stdc.h> using lllong long; ll n,m; void solve() {int x;std::cin>>x;ll ans0,y;for(int i1;i<x;i){if(std::__gcd(i,x)i>a…

探索Kimi模型AI:革新人工智能的未来

探索Kimi模型AI&#xff1a;革新人工智能的未来 人工智能&#xff08;AI&#xff09;技术的发展已经取得了巨大的进步&#xff0c;为我们的生活带来了许多便利和创新。在这个充满活力和竞争的领域中&#xff0c;Kimi模型AI以其独特的设计和功能吸引了人们的注意。本文将深入探…

图片浏览器-PicView

一、前言 PicView 是一款适用于 Windows 10 或 11 的快速高效的图像查看器&#xff0c;配备了干净简洁的用户界面&#xff0c;可以在不需要时方便地隐藏。 二、支持类型 它支持广泛的图像文件类型&#xff0c;包括&#xff1a;WEBP、GIF、SVG、PNG、JXL、HEIC、PSD 三、软件特…