手写C++ muduo库(Poller)

94 阅读2分钟

image.png

为什么muduo库要抽象一层poller?因为在EventLoop里面,它在使用IO复用并没有说直接指定epoll,因为muduo库对外提供的IO复用的能力包含两个:poll和epoll,在EventLoop里面不可能直接使用poll或者epoll它是从抽象层面直接使用poller,到时候引用不同的派生类对象,调用它们的同名覆盖方法,就可以非常方便的去扩展不同的IO复用能力,就是多路分发器。

image.png

poller监听的就是eventloop保存的那些channel
protected的成员变量就是让派生类可以访问到,private的成员变量派生类不能访问到。

Poller.h

#pragma once

#include "noncopyable.h"
#include "Timestamp.h"

#include<vector>
#include<unordered_map>

//只用到指针类型 
class Channel;
class EventLoop;

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

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

    //给所有IO复用保留统一的接口
    virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;//相当于启动了epoll_wait 
    virtual void updateChannel(Channel* channel) = 0;//相当于启动了epoll_ctrl 
    virtual void removeChannel(Channel* channel) = 0;//fd所感兴趣的事件delete掉 

    //判断当前Channel是否在Poller当中
    bool hasChannel(Channel *channel) const;

    //EventLoop可以通过该接口获取默认的IO复用的具体实现 
    static Poller* newDefaultPoller(EventLoop *loop);

protected:
    //map的key:sockfd  value:sockfd所属的channel通道类型
    using ChannelMap = std::unordered_map<int,Channel*>;
    ChannelMap channels_;
private:
    EventLoop *ownerLoop_; // 定义Poller所属的事件循环EventLoop
};

Poller.cc

#include "Poller.h"
#include "Channel.h"

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

为什么不把 newDefaultPoller写在Poller.cc? 如果真的把newDefaultPoller写在Poller.cc里面,从语法上来说,没有错误。 但是这个函数是要生成一个具体的I/O复用对象,并返回一个基类的指针。 所以就得用下面这2个头文件,才能去生成一个具体的实例对象并返回回去,这样不合理,因为继承结构中,poller是基类,只能派生类引用基类,基类不能引用派生类

image.png

那这个newDefaultPoller实现在哪呢?

image.png

muduo默认使用epoll

image.png 根据键返回对应的值

DefaultPoller.cc分解了poller和具体的epollpoller的强耦合

#include "Poller.h"

#include<stdlib.h>

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