前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >缩略muduo网络库(3)事件分发器poller

缩略muduo网络库(3)事件分发器poller

作者头像
看、未来
发布2021-10-09 15:33:25
4850
发布2021-10-09 15:33:25
举报

在muduo的EventLoop里面有一个poller和一堆的channel,其实这个poller是一个抽象基类,子类有poll和epoll。大抵是二者其实都有有点,可以兼顾吧。来看看其中巧夺天工的设计实现。

文章目录

纯虚基类 poller

代码语言:javascript
复制
#pragma once

#include "nocopyable.hpp"
#include "timestamp.hpp"

#include<vector>
#include<unordered_map>

class Channel;
class EventLoop;

//muduo中多路事件分发器的核心模块
class Poller:nocpoyable{
public:
    using ChannelList = std::vector<Channel*>;

    explicit Poller(EventLoop *loop);
    virtual ~Poller();

    virtual timestamp poll(int timeoutMS,ChannelList* activeChannels) = 0;
    virtual void updateChannel(Channel* channel) = 0;
    virtual void removeChannel(Channel* channel) = 0;
    
    bool hasChannel(Channel* channel) const;

    //获取当前循环中默认的poller
    static Poller* newDefaultPoller(EventLoop* loop);

protected:
    //key:sockfd  value:Channel通道类型
    using ChannelMap = std::unordered_map<int,Channel*>;
    ChannelMap channelmap_;

private:
    EventLoop *ownnerloop_; //定义poller所属事件循环
};
代码语言:javascript
复制
#include "poller.hpp"
#include "channel.hpp"
Poller::Poller(EventLoop* loop)
  : ownnerloop_(loop)
{
}

Poller::~Poller() = default;

bool Poller::hasChannel(Channel* channel) const
{
    auto it = channelmap_.find(channel->fd());
    return it != channelmap_.end() && it->second == channel;
}


//获取当前循环中默认的poller
// static Poller* newDefaultPoller(EventLoop* loop);
/*
    这个函数不能在这里实现,这个函数是要返回一个具体的子类的对象的
*/
代码语言:javascript
复制
#include "poller.hpp"

Poller* Poller::newDefaultPoller(EventLoop* loop)
{
    if (::getenv("MUDUO_USE_POLL")) //环境变量
    {
        return nullptr; //生成poll的实例
    }
    else
    {
        return nullptr; //生成epoll的实例
    }
}

子类:EpollPoller

代码语言:javascript
复制
#include"poller.hpp"
#include"timestamp.hpp"

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


/*
    epoll_create
    epoll_ctl
    epoll_wait
*/
class EpollPoller:public Poller{
public:
    EpollPoller(EventLoop* loop);   //epoll_create
    ~EpollPoller() override;    //覆盖基类,如果基类对应名称不是虚函数则无法这么使用

    timestamp poll(int timeoutMS,ChannelList* activeChannels) override; //epoll_wait
    void updateChannel(Channel* channel) override;  //epoll_ctl
    void removeChannel(Channel* channel) override;  //epoll_ctl
    
 
private:

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

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

    using EventList = std::vector<epoll_event>;
    
    static const int kINitEventListSize = 16;   //给epollevent一个初始长度
    int epollfd_;
    EventList events_;
};
代码语言:javascript
复制
#include"epollpoller.hpp"
#include"logger.hpp"
#include"channel.hpp"

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

//标识channel跟epoll的关系
const int kNew = -1;    
const int kAdded = 1;
const int kDeleted = 2;

EpollPoller::EpollPoller(EventLoop* loop)
    :Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
    events_(kINitEventListSize)
{
    if(epollfd_<0){
        LOG_FATAL("epoll_create error:%d\n",errno);
    }
}

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


//通过epoll_wait,将有事件的channel通过传出参数传递给EventLoop
timestamp EpollPoller::poll(int timeoutMS,ChannelList* activeChannels){
    LOG_DEBUG("func = %s, fd total count = %d\n",__FUNCITON__, channelmap_.size());

    int numEvents = ::epoll_wait(epollfd_,&*events_.begin(),events_.size(),timeoutMS);
    int saveErrno = errno;

    timestamp now(timestamp::now());

    if(numEvents > 0){  //如果有事件发生
        LOG_DEBUG("%d events happened \n",numEvents);
        fillActiveChannels(numEvents,activeChannels);

        //如果塞满了,说明需要扩容了
        if(numEvents == events_.size()){
            //需要手动扩容么?
            //难道刚开始是有分配了内存还是咋?不一直是自动扩容吗?
            //而且vector的扩容的话,1.5倍怕是要好一些吧
            events_.resize(events_.size()*2);
        }
    }
    else if(numEvents == 0){    //超时
        LOG_DEBUG("%s timeout\n",__FUNCTION__);
    }
    else{   //出问题
        if(saveErrno != EINTR){
            errno = saveErrno;
            LOG_ERROR("EpollPoller::poll err!\n");
        }
    }

    return now;
}


void EpollPoller::updateChannel(Channel* channel){
    int index = channel->index();
    int fd = channel->fd();

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

    //如果还没将channel添加到poller中,或已被删除过
    if(index == kNew || index == kDeleted){
        
        if(index == kNew){
            channelmap_[fd] = channel;
        }
        channel->set_index(kAdded);
       update(EPOLL_CTL_ADD,channel);
    }
    else{   //channel已经在poller上注册过了
        if (channel->isNoneEvent()){    //如果没有感兴趣的事件了
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted);
        }
        else{
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

    
void EpollPoller::removeChannel(Channel* channel){
    int index = channel->index();
    int fd = channel->fd();

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

    channelmap_.erase(fd);
    if(index == kAdded){
        update(EPOLL_CTL_DEL,channel);
    }
    channel->set_index(kNew);
}

void EpollPoller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const{
    
    //for(Channel* channel:activeChannels){
        //这样居然不行了!!!

    //eventloop 即将拿到它的poller返回的所有发生事件列表
    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); 
    }
}

void EpollPoller::update(int operation,Channel* channel){
    epoll_event event;
    int fd = channel->fd();
    
    memset(&event,0,sizeof(event));
    event.events = channel->events();
    event.data.ptr = channel;
    event.data.fd = fd;

    if(::epoll_ctl(epollfd_,operation,fd,&event)<0){
        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);
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/09/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 纯虚基类 poller
  • 子类:EpollPoller
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档