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

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缩略muduo网络库事件分发器poller相关的知识,希望对你有一定的参考价值。

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

纯虚基类 poller

#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所属事件循环
};
#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);
/*
    这个函数不能在这里实现,这个函数是要返回一个具体的子类的对象的
*/
#include "poller.hpp"

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

子类:EpollPoller

#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_;
};
#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);
        }
    }
}

以上是关于缩略muduo网络库事件分发器poller的主要内容,如果未能解决你的问题,请参考以下文章

缩略版muduo网络库:事件处理器 Chanel

缩略版muduo网络库:事件处理器 Chanel

Muduo网络库源码分析 EventLoop事件循环(Poller和Channel)

缩略muduo库:事件循环 EventLoop

缩略muduo库:事件循环 EventLoop

从实例看muduo网络库各模块交互过程