muduo源码剖析之poller/EpollPoller多路复用类

简介

poller是I/O多路复用接口抽象虚基类,对I/O多路复用API的封装,muduo提供了EPollPoller和PollPoller派生类(epoll和poll),所以不支持select.

newDefaultPoller()默认选择epoll

主要接口

poll

是Poller的核心功能,使用派生类的poll或者epoll_wait来阻塞等待IO事件发生
通过派生类的实现来填充EventLoop的activeChannelList_

static createNewPoller:

工厂函数,创建一个Poller实例
在EpollPoller中,每个实例对应一个epollfd

update

更新I/O多路复用的状态,例如epoll_ctl的ADD,MOD,DEL

主要成员

loop

控制当前Poller的EventLoop指针
其余成员由派生类实现

源码剖析

poller.h

#ifndef MUDUO_NET_POLLER_H
#define MUDUO_NET_POLLER_H#include <map>
#include <vector>#include "muduo/base/Timestamp.h"
#include "muduo/net/EventLoop.h"namespace muduo
{
namespace net
{class Channel;///
/// Base class for IO Multiplexing
///
/// This class doesn't own the Channel objects.
class Poller : noncopyable
{public:typedef std::vector<Channel*> ChannelList;Poller(EventLoop* loop);virtual ~Poller();/// Polls the I/O events./// Must be called in the loop thread.virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;/// Changes the interested I/O events./// Must be called in the loop thread.virtual void updateChannel(Channel* channel) = 0;/// Remove the channel, when it destructs./// Must be called in the loop thread.virtual void removeChannel(Channel* channel) = 0;//判断是否存在virtual bool hasChannel(Channel* channel) const;//创建一个poller,默认是epollstatic Poller* newDefaultPoller(EventLoop* loop);void assertInLoopThread() const{ownerLoop_->assertInLoopThread();}protected:typedef std::map<int, Channel*> ChannelMap;ChannelMap channels_;private:EventLoop* ownerLoop_;
};}  // namespace net
}  // namespace muduo#endif  // MUDUO_NET_POLLER_H

poller.cc

#include "muduo/net/Poller.h"
#include "muduo/net/Channel.h"using namespace muduo;
using namespace muduo::net;Poller::Poller(EventLoop* loop): ownerLoop_(loop)
{
}Poller::~Poller() = default;bool Poller::hasChannel(Channel* channel) const
{assertInLoopThread();ChannelMap::const_iterator it = channels_.find(channel->fd());return it != channels_.end() && it->second == channel;
}

EPollPoller.h

#ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H
#define MUDUO_NET_POLLER_EPOLLPOLLER_H#include "muduo/net/Poller.h"#include <vector>struct epoll_event;namespace muduo
{
namespace net
{///
/// IO Multiplexing with epoll(4).
///
class EPollPoller : public Poller
{public:EPollPoller(EventLoop* loop);~EPollPoller() override;Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;void updateChannel(Channel* channel) override;void removeChannel(Channel* channel) override;private:static const int kInitEventListSize = 16;static const char* operationToString(int op);void fillActiveChannels(int numEvents,ChannelList* activeChannels) const;void update(int operation, Channel* channel);typedef std::vector<struct epoll_event> EventList;int epollfd_;EventList events_;
};}  // namespace net
}  // namespace muduo
#endif  // MUDUO_NET_POLLER_EPOLLPOLLER_H

EPollPoller.cc

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.// Author: Shuo Chen (chenshuo at chenshuo dot com)#include "muduo/net/poller/EPollPoller.h"#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <sys/epoll.h>
#include <unistd.h>using namespace muduo;
using namespace muduo::net;/*struct epoll_event
{uint32_t events;   //Epoll eventsepoll_data_t data;    //User data variable
} __attribute__ ((__packed__));typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;*/// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
static_assert(EPOLLIN == POLLIN,        "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI,      "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT,      "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP,  "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR,      "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP,      "epoll uses same flag values as poll");namespace
{
const int kNew = -1; //channel尚未添加到poller中
const int kAdded = 1; //已经添加了
const int kDeleted = 2;   //之前监听过了,后来移除了监听
}//当flag = EPOLL_CLOEXEC,创建的epfd会设置FD_CLOEXEC
//FD_CLOEXEC表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递给exec创建的新进程
EPollPoller::EPollPoller(EventLoop* loop): Poller(loop),//创建epollfd,使用带1的版本//如果参数为0,则与epoll_create版本相同,设置为O_CLOEXEC,查看open函数的这个参数解释,//子进程fork并调用exec时会关闭这个fdepollfd_(::epoll_create1(EPOLL_CLOEXEC)),      events_(kInitEventListSize)                    //vector这样用时初始化kInitEventListSize个大小空间,默认16
{if (epollfd_ < 0)                           //在构造函数中判断,<0就abort(){LOG_SYSFATAL << "EPollPoller::EPollPoller";}
}EPollPoller::~EPollPoller()
{::close(epollfd_);
}Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)//ChannelList是一个存放channel的vector
{LOG_TRACE << "fd total count " << channels_.size();int numEvents = ::epoll_wait(epollfd_,&*events_.begin(),  //events_已初始化,是存放epoll_event的vectorstatic_cast<int>(events_.size()),    //监控套接字的数目timeoutMs);int savedErrno = errno;Timestamp now(Timestamp::now());if (numEvents > 0){LOG_TRACE << numEvents << " events happened";fillActiveChannels(numEvents, activeChannels);if (implicit_cast<size_t>(numEvents) == events_.size()) //如果返回的事件数目等于当前事件数组大小,就分配2倍空间{events_.resize(events_.size()*2);}}else if (numEvents == 0){LOG_TRACE << "nothing happened";}else{// error happens, log uncommon onesif (savedErrno != EINTR){errno = savedErrno;LOG_SYSERR << "EPollPoller::poll()";}}return now;
}//把返回到的这么多个事件添加到activeChannels
void EPollPoller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const   
{assert(implicit_cast<size_t>(numEvents) <= events_.size());for (int i = 0; i < numEvents; ++i)                          //确定它的大小小于events_的大小,因为events_是预留的事件vector{Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUGint fd = channel->fd();                       //debug时做一下检测ChannelMap::const_iterator it = channels_.find(fd);assert(it != channels_.end());assert(it->second == channel);
#endifchannel->set_revents(events_[i].events);        //把已发生的事件传给channel,写到通道当中activeChannels->push_back(channel);             //并且push_back进activeChannels}
}//这个函数被调用是因为channel->enablereading()被调用,再调用channel->update(),再event_loop->updateChannel(),再->epoll或poll的updateChannel被调用
//
void EPollPoller::updateChannel(Channel* channel)
{Poller::assertInLoopThread();  //在IO线程const int index = channel->index();  //初始状态index是-1LOG_INFO << "fd = " << channel->fd()<< " events = " << channel->events() << " index = " << index;// 当是新的或是之前监听过,后来移除了监听// 两者的区别在于,新的channel 之前没有在epoll 中保存// 而 del 的之前在 channels_ 中保存了,但是没有被放入epoll_ctl中监听if (index == kNew || index == kDeleted)  //index是在poll中是下标,在epoll中是三种状态,上面有三个常量{// a new one, add with EPOLL_CTL_ADDint fd = channel->fd();if (index == kNew){assert(channels_.find(fd) == channels_.end());  //channels_是一个Mapchannels_[fd] = channel;}else // index == kDeleted{assert(channels_.find(fd) != channels_.end());assert(channels_[fd] == channel);}channel->set_index(kAdded);update(EPOLL_CTL_ADD, channel);   //注册事件}else{// update existing one with EPOLL_CTL_MOD/DELint fd = channel->fd();(void)fd;assert(channels_.find(fd) != channels_.end());assert(channels_[fd] == channel);assert(index == kAdded);// 既然已经添加了,那么可能的修改就是修改监听的时间,或者不在监听// 因此这里先判断是否是没有监听的事件了,如果是那么直接移除、if (channel->isNoneEvent())   //判断无事件{update(EPOLL_CTL_DEL, channel);    //删除事件channel->set_index(kDeleted);  //删除后被设置为kDeleted}else{update(EPOLL_CTL_MOD, channel);   //修改已注册的监听事件}}
}void EPollPoller::removeChannel(Channel* channel)
{Poller::assertInLoopThread();  //判断是否在IO线程int fd = channel->fd();LOG_TRACE << "fd = " << fd;assert(channels_.find(fd) != channels_.end());assert(channels_[fd] == channel);assert(channel->isNoneEvent());int index = channel->index();assert(index == kAdded || index == kDeleted);size_t n = channels_.erase(fd);   //删除(void)n; assert(n == 1);if (index == kAdded){update(EPOLL_CTL_DEL, channel);}channel->set_index(kNew);
}void EPollPoller::update(int operation, Channel* channel)
{printf("-------%s,line.%d-------\n",__FUNCTION__,__LINE__);struct epoll_event event;    //存放数据的结构体memZero(&event, sizeof event);event.events = channel->events();  //注册的事件event.data.ptr = channel;int fd = channel->fd();LOG_INFO << "epoll_ctl op = " << operationToString(operation)<< " fd = " << fd << " event = { " << channel->eventsToString() << " }";if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)//epoll_ctl失败返回-1{if (operation == EPOLL_CTL_DEL){LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;}else{LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;}}
}const char* EPollPoller::operationToString(int op)
{switch (op){case EPOLL_CTL_ADD:return "ADD";case EPOLL_CTL_DEL:return "DEL";case EPOLL_CTL_MOD:return "MOD";default:assert(false && "ERROR op");return "Unknown Operation";}
}

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

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

相关文章

后端工程进阶| 青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天 并发编程 协程Goroutine通道Channel锁Lock 并发基础 串行程序与并发程序&#xff1a;串行程序特指只能被顺序执行的指令列表&#xff0c;并发程序则是可以被并发执行的两个及以上的串行程序的综合体。并发程序与并行程序…

Java设计模式之模板方法模式

目录 定义 结构 案例 优缺点 优点 缺点 使用场景 JDK源码解析 无法查看的无参read()原因 定义 定义一个操作中的算法骨架&#xff0c;而将算法的一些步骤延迟到子类中&#xff0c;使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。简单来说&#xf…

python---设计模式(单例模式和工厂模式)

单例模式 定义&#xff1a;保证一个类只有一个实例&#xff0c;并提供一个访问它的全局访问点。节省内存&#xff0c;节省创建对象的开销。 非单例模式 &#xff1a; class StrTools:passs1 StrTools() s2 StrTools() print(s1) print(s2) 单例模式 &#xff1a; # tr_t…

Ubuntu安装pyenv,配置虚拟环境

文章目录 安装pyenvpyenv创建虚拟环境一般情况下创建虚拟环境的方法 安装pyenv 摘自&#xff1a;文章 pyenv可以管理不同的python版本 1、安装pyenv的依赖库 # 执行以下命令安装依赖库 # 更新源 sudo apt-get update # 更新软件 sudo apt-get upgradesudo apt-get install ma…

二十、设计模式之迭代器模式

目录 二十、设计模式之迭代器模式能帮我们干什么&#xff1f;主要解决什么问题&#xff1f;优缺点优点缺点&#xff1a; 使用的场景角色 实现迭代器模式定义迭代器容器实现可迭代接口迭代器实现使用 总结 二十、设计模式之迭代器模式 所属类型定义行为型提供一种方法顺序访问一…

Postman如何导出接口的几种方法?

本文主要介绍了Postman如何导出接口的几种方法&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下 前言&#xff1a; 我的文章还是一贯的作风&#xff0c;简确用风格&#xff08;简单确实有用&#xff09;&a…

单片机核心/RTOS必备 (ARM汇编)

ARM汇编概述 一开始&#xff0c;ARM公司发布两类指令集&#xff1a; ARM指令集&#xff0c;这是32位的&#xff0c;每条指令占据32位&#xff0c;高效&#xff0c;但是太占空间。Thumb指令集&#xff0c;这是16位的&#xff0c;每条指令占据16位&#xff0c;节省空间。 要节…

Python操作MySQL基础使用

Python操作MySQL基础使用 import pymysql# 链接数据库 conn pymysql.connect(host10.5.6.250,port3306,userroot,password******** )# 查看MySQL版本信息 print(conn.get_server_info()) # 5.5.27# 获取到游标对象 cursor conn.cursor()# 选择数据库 conn.select_db("…

《排错》Python重新安装后,执行yum命令报错

安装完新的python以后&#xff0c;发现yum命令没法用 以下是报错信息&#xff1a; [rootmaster ~]# yum There was a problem importing one of the Python modules required to run yum. The error leading to this problem was:No module named yumPlease install a packag…

8.MySQL内外连接

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 表的内连和外连 内连接 外连接 左外连接 右外连接 我们进行演示的表结构是这样的&#xff1a; 表的内连和外连 内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们前面学习的…

asp.net core webapi signalR通信

1.前端使用npm 导入signalR的包和jquery包 npm install jquery -y npm install micosoft/signalr -y <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice…

C语言之函数详解

目录 函数的定义 函数的调用 变量的存储类型 auto自动变量 extern外部变量 static静态变量 register寄存器变量 函数的定义 在C语言中&#xff0c;函数是一段可重复使用的代码块&#xff0c;用于执行特定的任务。函数的定义包括函数的声明和函数体两个部分。 函数的声…

Redis快速上手篇(三)(事务+Idea的连接和使用)

Redis事务 可以一次执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的 所有命令都会序列化&#xff0c;按顺序地串行化执行而不会被其它命令插入&#xff0c;不许加塞。 单独的隔离的操作 官网说明 https://redis.io/docs/interact/transactions/ MULTI、EXEC、…

Python机器学习17——Xgboost和Lightgbm结合分位数回归(机器学习与传统统计学结合)

最近XGboost支持分位数回归了&#xff0c;我看了一下&#xff0c;就做了个小的代码案例。毕竟学术市场上做这种新颖的机器学习和传统统计学结合的方法还是不多&#xff0c;算的上创新&#xff0c;找个好数据集可以发论文。 代码实现 导入包 import numpy as np import pandas…

【转载】双亲委派模型

双亲委派模型是 Java 类加载器的一种工作模式&#xff0c;通过这种工作模式&#xff0c;Java 虚拟机将类文件加载到内存中&#xff0c;这样就保证了 Java 程序能够正常的运行起来。那么双亲委派模型究竟说的是啥呢&#xff1f;接下来我们一起来看。 1.类加载器 双亲委派模型针…

C++经典面试题:内存泄露是什么?如何排查?

1.内存泄露的定义&#xff1a;内存泄漏简单的说就是申请了⼀块内存空间&#xff0c;使⽤完毕后没有释放掉。 它的⼀般表现⽅式是程序运⾏时间越⻓&#xff0c;占⽤内存越多&#xff0c;最终⽤尽全部内存&#xff0c;整个系统崩溃。由程序申请的⼀块内存&#xff0c;且没有任何⼀…

FFmpeg -r 放在 -i 前后的区别

在 FFmpeg 中&#xff0c;-r 选项的位置对于帧率设置有所影响&#xff0c;具体取决于它是放在 -i 之前还是之后。 放在 -i 之前&#xff1a;如果将 -r 选项放在 -i 之前&#xff0c;则它将用于设置输入文件的帧率。这意味着它会告诉 FFmpeg 如何解析输入文件的帧率信息。例如&…

github.com/holiman/uint256 源码阅读

github.com/holiman/uint256 源码阅读 // uint256: Fixed size 256-bit math library // Copyright 2018-2020 uint256 Authors // SPDX-License-Identifier: BSD-3-Clause// Package math provides integer math utilities.package uint256import ("encoding/binary&…

HighCharts点击无响应问题

HighCharts 点击无响应问题 背景介绍 项目需要展示一个小时内日志设备的状态&#xff0c;由于数据量比较大&#xff0c;使用echarts效果不好。于是采用highcharts来处理显示。highcharts使用起来很方便&#xff0c;还有打印照片功能&#xff0c;相当满意。这里采用官网给的例…

产研团队必看!3款在线白板工具助你轻松改善工作!

随着科技的不断进步和团队协作的需求日益增加&#xff0c;产研团队在工作中常常面临各种挑战。例如&#xff0c;团队成员之间的沟通不畅、信息共享不便以及项目进度不明确等等。这些问题会导致团队的效率低下&#xff0c;影响整体工作质量。 为了解决这些问题&#xff0c;越来…