重写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.mzph.cn/web/7204.shtml

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

相关文章

从零开始学RSA:已知p+q和(p+1)(q+1)和已知p-q和n

(17)已知pq和(p1)(q1) 题目给出pq&#xff0c;(p1)(q1)&#xff0c;e和c。 首先需要求出phi&#xff0c;然后求解d&#xff0c;最后再求解m。 phi (p-1)(q-1) pq - (pq) 1 pq的值题目已经给出了&#xff0c;接下来只需要求出pq的值即可求出phi的值。题目还给出了(p1)(q1…

从OutputStream类看Java中的IO流操作

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

C#连接S7-200 smart通讯测试

honeytree 一、编程环境 VS2022软件&#xff0c;选择windows窗体应用&#xff08;.NET FrameWork&#xff09;&#xff1a;​博途TIA/WINCC社区VX群 ​博途TIA/WINCC社区VX群 添加NuGet程序包&#xff1b;S7netplus 二、引用http://S7.net 三、建立PLC链接 S7-200smart和…

使用Docker安装Jenkins

大家好&#xff0c;今天给大家分享如何使用docker安装jenkins&#xff0c;关于docker的安装和常用命令可以参考下面两篇文章&#xff0c;使用docker可以提高资源利用率&#xff0c;能够在不同的环境中轻松迁移和部署应用&#xff0c;在本文中就不过多赘述了。 Docker常用命令 …

工厂模式+策略模式完成多种登录模式的实现

前提 &#xff08;简单工厂不属于设计模式&#xff0c;而是一种编程思想【抽象一层出来】&#xff09;工厂方法模式、抽象工厂模式 以上都是为了解耦&#xff0c;如果考虑多个纬度&#xff08;如需要同时考虑多种电器&#xff0c;多种品牌&#xff09;则优先考虑抽象工厂。 …

怎么通过Java语言实现远程控制无人售货柜

怎么通过Java语言实现远程控制无人售货柜呢&#xff1f; 本文描述了使用Java语言调用HTTP接口&#xff0c;实现控制无人售货柜&#xff0c;独立控制售货柜、格子柜的柜门。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi控…

ASP.NET网上图书预约系统的设计

摘 要 《网上图书预约系统的设计》是以为读者提供便利为前提而开发的一个信息管理系统&#xff0c;它不仅要求建立数据的一致性和完整性&#xff0c;而且还需要应用程序功能的完备、易用等特点。系统主要采用VB.NET作为前端的应用开发工具&#xff0c;利用SQL Server2000数据…

7-35 有理数均值

题目链接&#xff1a;7-35 有理数均值 一. 题目 1. 题目 2. 输入输出样例 3. 限制 二、代码 1. 代码实现 #include <iostream> using namespace std;// 计算公约数 int calGcd(int a, int b) {int gcd;bool negative false;if (a a / b * b) { // b整除areturn b;}…

Llama3-Tutorial之Llama3本地Web Demo部署

Llama3-Tutorial之Llama3本地 Web Demo部署 Llama3-Tutorial之Llama3本地Web Demo部署章节。 参考&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 1. 环境配置 conda create -n llama3 python3.10conda activate llama3conda install pytorch2.1.2 torchvision0…

【RAG 论文】SKR:Self-Knowledge 指导下的 RAG

论文&#xff1a;Self-Knowledge Guided Retrieval Augmentation for Large Language Models ⭐⭐⭐⭐ Tsinghua, arXiv:2310.05002 文章目录 一、论文速读二、实现细节2.1 数据的收集2.2 引出 LLM 的 Self-Knowledge 的方法1&#xff09;Direct Prompting2&#xff09;In-Cont…

【微服务 开发】微服务介绍,服务拆分,远程调用

微服务 微服务SpringCloud 拆分如何拆分 远程调用 微服务 微服务是一种软件架构风格&#xff0c;它是以专注于单一职责的很多小型项目为基础&#xff0c;组合成复杂的大型应用 单体架构 将业务的所有功能集中在一个项目中进行开发&#xff0c;打成一个包部署 微服务的特征&…

stm32f103zet6_DAC_1_介绍

STM32微控制器系列的DAC&#xff08;数字到模拟转换器&#xff09;功能是其片上外设之一&#xff0c;用于将数字信号转换为模拟信号。DAC在许多应用中都非常有用&#xff0c;例如音频输出、模拟信号生成、闭环控制系统中作为模拟输出等。 STM32微控制器的DAC功能特点包括&…

2024-3-23 青少年软件编程(C语言)等考(三级)解析

2024-3-23 青少年软件编程(C语言)等级考试试卷(三级)解析1、我家的门牌号 我家住在一条短胡同里,这条胡同的门牌号从1开始顺序编号。 若所有的门牌号之和减去我家门牌号的两倍,恰好等于n,求我家的门牌号及总共有多少家。 数据保证有唯一解。 时间限制:1000 内存限制:6…

概率论 科普

符号优先级 概率公式中一共有三种符号&#xff1a;分号 ; 、逗号 , 、竖线 | 。 ; 分号代表前后是两类东西&#xff0c;以概率P(x;θ)为例&#xff0c;分号前面是x样本&#xff0c;分号后边是模型参数。分号前的 表示的是这个式子用来预测分布的随机变量x&#xff0c;分号后的…

(论文阅读-优化器)Orca: A Modular Query Optimizer Architecture for Big Data

目录 摘要 一、简介 二、背景知识 2.1 大规模并行处理 2.2 SQL on Hadoop 三、Orca架构 四、查询优化 4.1 优化工作流 4.2 并行查询优化 五、Metadata Exchange 六、可行性 6.1 Minimal Repros 6.2 优化器准确性测试 七、实验 八、相关工作 8.1 查询优化基础 8…

自动驾驶融合定位:IMU内参模型及标定

自动驾驶融合定位&#xff1a;IMU内参模型及标定 一、 概述 标定的本质是参数辨识。首先明确哪些参数可辨识&#xff0c;其次弄清怎样辨识。 参数包括陀螺仪和加速度计各自的零偏、标度因数、安装误差。 辨识就比较丰富了&#xff0c;如果让各位先不局限于标定任务&#xf…

CasaOS玩客云安装memos开源云笔记并实现随时随地远程记笔记

文章目录 前言1. 使用Docker部署memos2. 注册账号与简单操作演示3. 安装cpolar内网穿透4. 创建公网地址5. 创建固定公网地址 前言 本文主要介绍如何在CasaOS玩客云&#xff0c;使用Docker本地部署21.6K stars的热门开源云笔记服务memos&#xff0c;并结合cpolar内网穿透工具打…

libevent的使用

文章目录 libevent封装的框架思想常用函数分析使用fifo的读写未决和非未决bufferevent特性bufferevent函数客户端和服务器连接和监听libevent实现socket通信 libevent封装的框架思想 libevent框架&#xff1a;1. 创建 event_base (乐高底座)2. 创建 事件evnet 3. 将事件 添加…

c++ 线程概述

C中的线程是并发编程的重要组成部分&#xff0c;它允许程序同时执行多个任务。以下是对C线程的概述&#xff1a; 基本概念&#xff1a; 并发&#xff1a;意味着两个或多个任务同时执行。在单核CPU上&#xff0c;由于只有一个CPU&#xff0c;某一时刻只能执行一个任务&#xff0…

MATLAB 变换

MATLAB 变换&#xff08;Transforms&#xff09; MATLAB提供了用于处理诸如Laplace和Fourier变换之类的变换的命令。转换在科学和工程中用作简化分析和从另一个角度查看数据的工具。 例如&#xff0c;傅立叶变换允许我们将表示为时间函数的信号转换为频率函数。拉普拉斯变换使…