Muduo源码分析一: Poller类 + EpollPoller类详解

前言

最近开始看陈硕大佬的muduo, 在这里记下所学到的知识叭. 第一次写关于阅读源代码的博客, 可能更多的是通过注释的方式来解释作者的思路, 如果有任何的疑问或意见, 欢迎评论.

正文

Poller介绍

eventloop类中有一个成员叫做poller_, 这个poller_负责着所有的IO事件, 在整个eventloop中起到了决定性的作用.

  std::unique_ptr poller_;	// eventloop.h

Poller.h介绍

这个Poller类只是一个抽象类, 因此我们简单的看一下即可, 具体解释看注释

class Poller : noncopyable
{
 public:
  typedef std::vector ChannelList;

  /// 构造函数
  Poller(EventLoop* loop);  
  virtual ~Poller();

  /// 必须由子类来进行实现
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;	// poll函数
  virtual void updateChannel(Channel* channel) = 0;	// 更新Channel, Channel是一个对文件描述符封装后的类
  virtual void removeChannel(Channel* channel) = 0;	// 移除Channel
  
  virtual bool hasChannel(Channel* channel) const;	// 是否存在参数所指的Channel
  static Poller* newDefaultPoller(EventLoop* loop);	// 在这里会选择epoll或者poll, 并i企鹅

  void assertInLoopThread() const	// 确保所有的操作都在事件循环的线程中
  {
    ownerLoop_->assertInLoopThread();	// 其实调用了eventloop中的assertInLoopThread()
  }

 protected:
  typedef std::map ChannelMap;	// 文件描述符和Channel对象的映射
  ChannelMap channels_;	// Channel的集合

 private:
  EventLoop* ownerLoop_;	// 每个Poller都要有自己的eventloop
};

DefaultPoller.cc介绍

这里是一个选择器, 根据系统环境不同而选择epoll或者poll. 因为现在的Linux环境基本都支持epoll, 所以我们在此只关注Epollpoller的实现.

Poller* Poller::newDefaultPoller(EventLoop* loop) //   使用newDefaultPoller 来包装poll/epoll类
{
  if (::getenv("MUDUO_USE_POLL"))
  {
    return new PollPoller(loop);
  }
  else
  {
    return new EPollPoller(loop);
  }
}

EpollPoller.h 介绍

EpollPoller 则是真正的实现部分, 头文件和Poller.h相比, 并没有多大变化, 具体的实现请看下节的EpollPoller.cc介绍.

class EPollPoller : public Poller
{
 public:
  EPollPoller(EventLoop* loop);	// 构造函数, 绑定一个eventloop
  ~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;	// 初始的事件列表大小为16

  static const char* operationToString(int op);

  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const;	// 把有IO的Channel放入到events_中
  void update(int operation, Channel* channel);

  typedef std::vector EventList;	

  int epollfd_;	// epoll的文件描述符
  EventList events_;		// 一次poll所得到的事件集合
};

EpollPoller.cc介绍

在阅读EpollPoller.cc时, 一定要考虑epoll的操作. 在这个类中, 还有一些和Channel 类等相关的操作, 在以后的博客中介绍.

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include "muduo/net/poller/EPollPoller.h"

#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"

#include 
#include 
#include 
#include 
#include 

using namespace muduo;
using namespace muduo::net;

// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
static_assert(EPOLLIN == POLLIN,        "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI,      "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT,      "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP,  "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR,      "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP,      "epoll uses same flag values as poll");

namespace
{
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_SYSFATAL << "EPollPoller::EPollPoller";
  }
}

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

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_TRACE << "fd total count " << channels_.size(); // 所有的需要监听的channel
  int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin()/* events的地址 */,
                               static_cast(events_.size()),  /*IO事件的大小*/ 
                               timeoutMs /* 间隔时间 */);
  int savedErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happened";
    fillActiveChannels(numEvents, activeChannels);  // 把这次有IO事件的channel加入到activeChannel中
    if (implicit_cast(numEvents) == events_.size()) //  动态扩容
    {
      events_.resize(events_.size()*2);
    }
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << "nothing happened";
  }
  else
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;
}

void EPollPoller::fillActiveChannels(int numEvents,
                                     ChannelList* activeChannels) const
{
  assert(implicit_cast(numEvents) <= events_.size()); // 防错处理
  for (int i = 0; i < numEvents; ++i) // 遍历!
  {
    Channel* channel = static_cast(events_[i].data.ptr);  // 将channel变量指向第i个event中去
#ifndef NDEBUG
    int fd = channel->fd(); // 得到channel中的fd
    ChannelMap::const_iterator it = channels_.find(fd); // 得到迭代器
    assert(it != channels_.end());  
    assert(it->second == channel);  
#endif
    channel->set_revents(events_[i].events); // 将事件的类别赋给channel中的revents_, revents_即是现在发生的IO事件
    activeChannels->push_back(channel); // 放入activeChannels中去
  }
}

void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread(); // 确保是在Loop线程中
  const int index = channel->index(); // 得到类型
  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中没有
      channels_[fd] = channel;  // 新增
    }
    else // index == kDeleted
    {
      assert(channels_.find(fd) != channels_.end());  // 确保channels中没有
      assert(channels_[fd] == channel); // 确保一致性
    }

    channel->set_index(kAdded); // 将类型设置为kAdded
    update(EPOLL_CTL_ADD, channel); // 操作epoll_ctl
  }
  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()) // 只是存在, 但没有对其IO事件进行任何的监听
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted); // 从epoll监听的fd中删除
    }
    else
    {
      update(EPOLL_CTL_MOD, channel); // 操作epoll_ctl
    }
  }
}

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); // 从channels中删除
  (void)n;
  assert(n == 1);

  if (index == kAdded)  // 如果是已被epoll监听, 则还需要从其监听fd中删除
  {
    update(EPOLL_CTL_DEL, channel);
  }
  channel->set_index(kNew); // 设置为kNew
}

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

const char* EPollPoller::operationToString(int op)
{
  switch (op)
  {
    case EPOLL_CTL_ADD:
      return "ADD";
    case EPOLL_CTL_DEL:
      return "DEL";
    case EPOLL_CTL_MOD:
      return "MOD";
    default:
      assert(false && "ERROR op");
      return "Unknown Operation";
  }
}

你可能感兴趣的:(Muduo源码)