手写C++ muduo库(EPollPoller事件分发器)

136 阅读4分钟

image.png

EPollPoller的主要实现:作为poller的派生类,把基类给派生类保留的这些纯虚函数的接口实现出来。 image.png overide表示在派生类里面,这些方法是覆盖方法。必须由编译器来保证在基类里面一定有这些函数的接口的声明。在派生类要重写他们。 给EPollPoller的析构函数写overide,就是让编译器给你检查基类的析构一定是虚函数。 底层是vector,放eventlist,可以动态地扩容。 成员变量的epollfd要通过epoll_create来创建,映射的就是epoll底层的文件系统

image.png epoll_wait第二个参数放的是最终发生事件的fd,返回值就是发生事件的fd的数量。

EPollPoller.h

#pragma once

#include "Poller.h"
#include "Timestamp.h"

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

class Channel;

/*
epoll使用:
1. epoll_create创建fd
2. epoll_ctl添加想让epoll监听的fd以及针对fd所感兴趣的事件,进行add/mod/del
3. epoll_wait
*/
class EPollPoller : public Poller
{
public:
    //表示epoll_create行为
    EPollPoller(EventLoop* loop);
    ~EPollPoller() override;

    //重写基类Poller的抽象方法
    //表示epoll_wait行为
    Timestamp poll(int timeoutMs, ChannelList *activeChannels);
    //下面两个方法表示epoll_ctl的行为
     void updateChannel(Channel* channel);
     void removeChannel(Channel* channel);
private:
    static const int kInitEventListSize = 16;//初始化vector长度 

    using EventList = std::vector<epoll_event>;

    //填写活跃的连接
    void fillActiveChannels(int numEvents,ChannelList *activeChannels) const;
    //更新Channel通道
    void update(int operation,Channel *channel);

    int epollfd_;
    EventList events_;//epoll_wait的第二个参数 
};

image.png epoll_create的参数size没有意义了,但是必须是大于0的数
epoll_create1,当我们去使用epoll_create1的时候,创建的epollfd,然后在当前线程里面再去fork创建一个子进程,然后用exec替换子进程,在子进程里面就把父进程设置成标志的fd,资源就都给关闭了。

image.png 结构体的fd就是epoll要监听的事件,ptr就是针对fd携带的数据,相当于是fd对于的channel

image.png

EpollPoller.cc

#include "EPollPoller.h"
#include "Logger.h"

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

//channel的成员变量index_表示在Poller/EpollPoller中的状态
const int kNew = -1; // 一个Channel还没有添加到Poller里面,channel的成员index_ = -1
const int kAdded = 1; //一个Channel已经添加到Poller里面
const int kDeleted = 2; // 一个Channel已经从epoll红黑树中删除了,但是还存在于poller的map中

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_);
}
//eventloop会创建一个channellist,并把创建好的channellist的地址传给poll
//poll通过epoll_wait监听到哪些fd发生了事件,把真真正正发生事件的channel通过形参发送到eventloop提供的实参里面
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
    //实际上应该用LOG_DEBUG输出日志更合理,可以设置开启或者不开启 
    LOG_INFO("func=%s fd total count:%ld\n",__FUNCTION__,channels_.size());
    //对poll的执行效率有所影响
    int numEvents = ::epoll_wait(epollfd_,&*events_.begin(),static_cast<int>(events_.size()),timeoutMs);
    //events_.begin()返回首元素的迭代器(数组),也就是首元素的地址,是面向对象的,要解引用,就是首元素的值,然后取地址 
    //就是vector底层数组的起始地址   static_cast类型安全的转换   timeoutMs超时时间 
    int saveErrno = errno;//全局的变量errno,库里的,poll可能在多个线程eventloop被调用 ,所以有局部变量存起来 
    Timestamp now(Timestamp::now());//获取当前时间

    if(numEvents > 0) //表示有已经发生相应事件的个数 
    {
        LOG_INFO("%d events happened \n",numEvents);
        fillActiveChannels(numEvents,activeChannels);
        if(numEvents == events_.size())
        {
            events_.resize(events_.size() * 2); //所有的监听的event都发生事件了,得扩容了 
        }
    }
    else if(numEvents == 0) //epoll_wait这一轮监听没有事件发生,timeout超时了 
    {
        LOG_DEBUG("%d timeout! \n",__FUNCTION__);
    }
    else 
    {
        if(saveErrno != EINTR) //不等于外部的中断 ,是由其他错误类型引起的 
        {
            errno = saveErrno; //适配 ,把errno重置成当前loop之前发生的错误的值 
            LOG_ERROR("EPollPoller::poll() err!");
        }
    }
    return now;
}
//下面两个方法表示epoll_ctl的行为
//channel update remove => Eventloop updateChannel removeChannel => Poller updateChannel removeChannel
/**
 *            EventLoop  =>   poller.poll
 *     ChannelList      Poller
 *                     ChannelMap  <fd, channel*>   epollfd
 * EventLoop里面有一个ChannelList,就是所有的Channel都是在EventLoop里面管理的,
 * Channel创建以后,向Poller里面注册的和未注册过的全部放在EventLoop里面的ChannelList,
 * 如果某些Channel向Poller里面注册过了,那么这些Channel会写到Poller里面的成员变量ChannelMap里面,
 * poll方法还是通过EventLoop来调用Poller.poll
 * poll函数的作用是通过epoll_wait监听到哪些fd发生事件,也就是哪些channel发生事件,
 * 把真正发生事件的channel通过形参填到EventLoop提供的实参里面
*/
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) //未添加或者已删除
    {
        //注意:kDeleted状态表示从epoll红黑树上删除,但是还存在于map表中
        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(channels_[fd]->isNoneEvent()) //已经对任何事件不感兴趣,不需要poller帮忙监听了 
        {
            //只是从红黑树上删除了,还存在于map表中
            channel->set_index(kDeleted);
            update(EPOLL_CTL_DEL,channel);
        }
        else
        {
            update(EPOLL_CTL_MOD,channel); //包含了fd的事件,感兴趣 
        }
    }
}
//从Poller中删除Channel
void EPollPoller::removeChannel(Channel* channel)
{
    int fd = channel->fd();
    channels_.erase(fd); //从map中删掉 

    LOG_INFO("func=%s => fd=%d\n",__FUNCTION__,channel->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_events(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;
    bzero(&event,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 delete error:%d\n",errno);
        }
        else //添加或者更改错误,这个会自动exit 
        {
            LOG_FATAL("epoll_ctl add/mod error:%d\n",errno);
        }
    }
}