muduo中的EPollPoller反应堆类

一、EPollPoller.h

EPollPoller的主要功能是实现Poller类保留的纯虚函数接口poll(epoll_wait)、updateChannel(epoll_ctl)、removeChannel(epoll_ctl)
在这里插入图片描述
muduo中EPollPoller.h如下:

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;  // EventList底层是vector,放epoll_event,可以动态地扩容

  int epollfd_;  // 成员变量的epollfd要通过epoll_create来创建,映射的就是epoll底层的红黑树文件系统
  EventList events_;
};

overirde表示在派生类里面,这些方法是覆盖方法,必须由编译器来保证在基类里面一定有这些函数的接口的声明。派生类要重写他们。
给EPollPoller的析构函数写overide,就是让编译器给你检查基类的析构函数一定是虚函数

成员变量的epollfd_要通过epoll_create来创建,映射的就是epoll底层的红黑树文件系统,即内核事件表

重写EPollPoller.h

#pragma once

#include "Poller.h"
#include "Timestamp.h"
 
#include <vector>
#include <sys/epoll.h>

class Channel;

// 继承Poller的时候需要知道Poller的实际结构,而不仅仅是一个类型声明
class EPollPoller : public Poller{
    public:
        EPollPoller(EventLoop* loop);
        ~EPollPoller() override;

        // epoll_wait
        Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;
        // epoll_ctl 更新感兴趣的事件 EPOLL_CTL_ADD  EPOLL_CTL_MOD  
        void updateChannel(Channel* channel) override;
        // eventloop中删除channel(fd)  EPOLL_CTL_DEL
        void removeChannel(Channel* channel) override;

    private:
        static const int kInitEventListSize = 16;

        // 填写活跃的连接,被poll方法调用
        void fillActiveChannels(int numEvents, ChannelList* activeChannels) const;
        // 更新channel,被updateChannel()和removeChannel()调用
        void update(int operation, Channel* channel);

        using EventList = std::vector<epoll_event>;

        int epollfd_;       // epoll_create创建,对应内核时间表
        EventList events_;  // 存放epoll_event事件的vector
};

二、EPollPoller::updateChannel

标识Channel状态的三个变量

const int kNew = -1;      // Channel从来没有添加到epoll或者从epoll和Poller中的map都删除    Channel的成员index_初始化-1
const int kAdded = 1;     // Channel已经添加到了epoll和Poller中的map
const int kDeleted = 2;   // Channel从epoll红黑树中删除了,但是还存在于Poller的map中

在这里插入图片描述
在这里插入图片描述
Linux2.6.8之后epoll_create的参数size就没有意义了,但是必须是大于0的数
在这里插入图片描述
当我们使用epoll_create1的时候,传入EPOLL_CLOEXEC,创建的epollfd,然后在当前线程里面再去fork创建一个子进程,然后用exec替换子进程(默认子进程继承了父进程所有的fd),在子进程里面就把父进程的fd都关闭,使得fd的引用计数减1。

muduo updateChannel 源码

void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  const int index = channel->index();  // 获取channel的状态,kNew、kAdded、kDeleted
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted)
  {
    // a new one, add with EPOLL_CTL_ADD
    int fd = channel->fd();
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel;  // 如果从未添加到EPollPoller中,那么将fd和对应channel对象添加到Poller中的成员map中
    }
    else // index == kDeleted
    {
      // 如果这个channel在Poller中已经被删除了,需要确保fd还在EventLoop,而且可以重新添加到Poller
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }

    channel->set_index(kAdded);     // 设置channel状态为kAdded
    update(EPOLL_CTL_ADD, channel); // 往Poller上注册channel,就是调用epoll_ctl往epoll红黑树中添加fd
  }
  else
  {
    // update existing one with EPOLL_CTL_MOD/DEL
    int fd = channel->fd();
    (void)fd;
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(index == kAdded);
    if (channel->isNoneEvent())
    {
      // 空事件,当前channel对任何事件都不感兴趣
      update(EPOLL_CTL_DEL, channel);  // 从当前Poller中删除当前channel,相当于从epoll红黑树中删除fd
      channel->set_index(kDeleted);
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);  // 如果channel已经存在于Poller,并且channel还对事件感兴趣,就是更新epoll红黑树上fd对应的事件
    }
  }
}

在这里插入图片描述

在这里插入图片描述

三、EPollPoller::update

调用epoll_ctl,EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD

muduo update源码

// operation 就是 EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD  
void EPollPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  memZero(&event, sizeof event);
  event.events = channel->events();
  event.data.ptr = channel; // channel挂在epoll_event.epoll_data.ptr
  int fd = channel->fd();
  LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
    << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
  {
    if (operation == EPOLL_CTL_DEL)
    {
      LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
    else
    {
      LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
  }
}

重写update

// epoll_ctl  EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD  更新channel
void EPollPoller::update(int operation, Channel* channel){
    epoll_event event;
    memset(&event,  0, sizeof(event));

    int fd = channel->fd();

    event.events = channel->events();
    event.data.fd = fd;
    event.data.ptr = channel;

    if(::epoll_ctl(epollfd_, operation, fd, &event) < 0){
        // epoll_ctl返回值小于0,则出错
        if(operation == EPOLL_CTL_DEL){
            LOG_ERROR("epoll_ctl delete error:%d\n", errno);
        }else{
            LOG_FATAL("epoll_ctl add / modify error:%d\n", errno);
        }
    }
}

四、EPollPoller::removeChannel

removeChannel源码

void EPollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  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);
}

重写removeChannel

// eventloop中删除channel(fd)  epoll_ctl  EPOLL_CTL_DEL
void EPollPoller::removeChannel(Channel* channel){
    int fd = channel->fd();
    channels_.erase(fd);   // 先在Poller里的map中删除

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

    int index = channel->index();
    if(index == kAdded){
        update(EPOLL_CTL_DEL, channel); // 从epoll红黑树中删除
    }
    channel->set_index(kNew);
}

五、EPollPoller::poll

调用epoll_wait,把发生事件的fd以及对应的Channel添加到activeChannels中

EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel通过activeChannels填到EventLoop的成员变量ChannelList中

// EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel通过activeChannels填到EventLoop的成员变量ChannelList中
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){
    // 实际上应该用LOG_DEBUG更合适
    LOG_INFO("func=%s => fd total count : %d \n", __FUNCTION__, channels_.size());
    // events_.begin()返回首元素的迭代器,也就是首元素的地址,是面向对象的,解引用后就是首元素的值,然后取地址就得到了vector封装的底层数组的首地址
    // epoll_wait返回后,events_底层的数组的前numEvents元素就是所有发生事件的epoll_event
    int numEvents = ::epoll_wait(epollfd_, &(*events_.begin()), static_cast<int>(events_.size()), timeoutMs);
    // 全局变量errno,poll可能在多个线程中的eventloop被调用,被读写,所以先用局部变量存起来
    int saveErrno = errno;
    Timestamp now(Timestamp::now());

    if(numEvents > 0){
        LOG_INFO("%d events happened \n", numEvents);
        fillActiveChannels(numEvents, activeChannels);  //  这里每次传入的都是一个空vector,把发生事件的Channel添加到EventLoop的成员变量ChannelList中
        if(numEvents == (int)events_.size()){
            // 发生事件的fd和成员变量EventList的长度相同,需要扩容
            events_.resize(events_.size() * 2);
        }
    }else if(numEvents == 0){
        // 没有事件发生,超时
        LOG_DEBUG("%s timeout! \n", __FUNCTION__);
    }else{
        if(saveErrno != EINTR){
            // 不等于外部的中断,是由其他错误类型引起的
            errno = saveErrno;  // 把全局变量errno重新置为当前错误saveErrno
            LOG_ERROR("EPollPoller::poll error!");
        }
    }
    return now;
}

fillActiveChannels填写活跃的连接

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_revents(events_[i].events);
        activeChannels->push_back(channel);     // EventLoop这样就拿到了它的Poller返回的所有发生事件的Channel列表了
    }
}

六、EPollPoller.cc

#include "EPollPoller.h"
#include "Channel.h"
#include "Logger.h"
#include "Timestamp.h"

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

// 标识channel的状态 
const int kNew = -1;      // Channel从来没有添加到epoll或者从epoll和Poller中的map都删除    Channel的成员index_初始化-1
const int kAdded = 1;     // Channel已经添加到了epoll和Poller中的map
const int kDeleted = 2;   // Channel从epoll红黑树中删除了,但是还存在于Poller的map中

// 构造函数,调用了epoll_create
EPollPoller::EPollPoller(EventLoop* loop)
    : Poller(loop)
    , epollfd_(epoll_create1(EPOLL_CLOEXEC))
    , events_(kInitEventListSize)  // vector<epoll_event>
{
    if(epollfd_ < 0){
        LOG_FATAL("epoll_create error:%d \n", errno);
    }
}

EPollPoller::~EPollPoller(){
    // epoll结束的时候,关闭内核事件表fd
    ::close(epollfd_);
}

// EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel(即活跃的)通过activeChannels填到EventLoop的成员变量ChannelList中
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){
    // 实际上应该用LOG_DEBUG更合适
    LOG_INFO("func=%s => fd total count : %lu \n", __FUNCTION__, channels_.size());
    // events_.begin()返回首元素的迭代器,也就是首元素的地址,是面向对象的,解引用后就是首元素的值,然后取地址就得到了vector封装的底层数组的首地址
    // epoll_wait返回后,events_底层的数组的前numEvents元素就是所有发生事件的epoll_event
    int numEvents = ::epoll_wait(epollfd_, &(*events_.begin()), static_cast<int>(events_.size()), timeoutMs);
    // 全局变量errno,poll可能在多个线程中的eventloop被调用,被读写,所以先用局部变量存起来
    int saveErrno = errno;
    Timestamp now(Timestamp::now());

    if(numEvents > 0){
        LOG_INFO("%d events happened \n", numEvents);
        fillActiveChannels(numEvents, activeChannels);  //  这里每次传入的都是一个空vector,把发生事件的Channel添加到EventLoop的成员变量ChannelList activeChannels中
        if(numEvents == (int)events_.size()){
            // 发生事件的fd和成员变量EventList的长度相同,需要扩容
            events_.resize(events_.size() * 2);
        }
    }else if(numEvents == 0){
        // 没有事件发生,超时
        LOG_DEBUG("%s timeout! \n", __FUNCTION__);
    }else{
        if(saveErrno != EINTR){
            // 不等于外部的中断,是由其他错误类型引起的
            errno = saveErrno;  // 把全局变量errno重新置为当前错误saveErrno
            LOG_ERROR("EPollPoller::poll error!");
        }
    }
    return now;
}

// Channel调用update remove ==> EventLoop updateChannel removeChannel ==> Poller updateChannel removeChannel
// epoll_ctl  EPOLL_CTL_ADD  EPOLL_CTL_MOD
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){
        if(index == kNew){
            // 如果从未添加到EPollPoller中,那么将fd和对应channel对象注册到Poller中的成员map中
            int fd = channel->fd();
            channels_[fd] = channel;
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel); // epoll_ctl EPOLL_CTL_ADD
    }else{
        // index == kAdded
        int fd = channel->fd();
        if(channel->isNoneEvent()){
            // 对任何事件都不感兴趣,从epoll红黑树中删除
            channel->set_index(kDeleted);
            update(EPOLL_CTL_DEL, channel);
        }else{
            // 对某些事件感兴趣
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

// eventloop中删除channel(fd)  epoll_ctl  EPOLL_CTL_DEL
void EPollPoller::removeChannel(Channel* channel){
    int fd = channel->fd();
    channels_.erase(fd);   // 先在Poller里的map中删除

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

    int index = channel->index();
    if(index == kAdded){
        update(EPOLL_CTL_DEL, channel); // 从epoll红黑树中删除
    }
    channel->set_index(kNew);  // 最终的结果就是要使得Channel的状态是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_revents(events_[i].events);
        activeChannels->push_back(channel);     // EventLoop就拿到了它的Poller返回的所有发生事件的Channel列表了
    }
}
 
// epoll_ctl  EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD  更新channel
void EPollPoller::update(int operation, Channel* channel){
    epoll_event event;
    memset(&event,  0, sizeof(event));

    int fd = channel->fd();

    event.events = channel->events();
    event.data.fd = fd;
    event.data.ptr = channel;

    if(::epoll_ctl(epollfd_, operation, fd, &event) < 0){
        // epoll_ctl返回值小于0,则出错
        if(operation == EPOLL_CTL_DEL){
            LOG_ERROR("epoll_ctl delete error:%d\n", errno);
        }else{
            LOG_FATAL("epoll_ctl add / modify error:%d\n", errno);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值