muduo库的EpollPoller剖析


EpollPoller和PollPoller一样,都是muduo库对I/O复用机制的封装,不过默认使用的是EpollPoller。在EventLoop中初始化构造poller_,调用newDefaultPoller(this),构造默认的poller。

EventLoop::EventLoop()
  : looping_(false),  //表示还未循环
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),   //赋值真实id
    poller_(Poller::newDefaultPoller(this)),   //构造了一个实际的poller对象
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this, wakeupFd_)),
    currentActiveChannel_(NULL)

newDefaultPoller内部是这样的:

Poller* Poller::newDefaultPoller(EventLoop* loop)
{
  if (::getenv("MUDUO_USE_POLL"))   //看环境变量,如果没有MUDUO_UER_POLL,默认epoll
  {
    return new PollPoller(loop);
  }
  else
  {
    return new EPollPoller(loop);
  }
}

也就是说,如果我们没有主动设置环境变量为MUDUO_USE_POLL,我们默认选择为构造EpollPoller。

EpollPoller重写了父类的三个函数:

virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);
virtual void updateChannel(Channel* channel);
virtual void removeChannel(Channel* channel);

EpollPoller的私有成员有:

private:
  static const int kInitEventListSize = 16;   //默认事件数组大小,是用来装epoll_wait()返回的可读或可写事件的
  									

  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;

  int epollfd_;
  EventList events_;  //用来存发生事件的数组

其中kInitEventListSize是epoll模式中用来盛装返回的发生事件的数组大小,就是events_,它在构造函数中会被初始化,看下面:

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),   //创建epollfd,使用带1的版本
    events_(kInitEventListSize)    //vector这样用时初始化kInitEventListSize个大小空间
{
  if (epollfd_ < 0)   //在构造函数中判断,<0就abort()
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}
有一点需要注意,就是vector的初始化方式,这样可以预分配大小。至于我们以后不断向epoll中注册事件,然后导致返回的发生事件可能越来越多,events_装不下了,muduo也有自己的处理,是在EpollPoller的核心函数poll()函数中处理的:

  if (implicit_cast<size_t>(numEvents) == events_.size())  //如果返回的事件数目等于当前事件数组大小,就分配2倍空间
    {																
      events_.resize(events_.size()*2);   
    }														       

所以不必担心vector的大小问题了,后续会以乘以2倍的方式分配,这也是内存分配的常见做法。

我们再来回忆一下事件处理流程,先看一下事件注册图:


当某个客端,比如TCPConnection,或者我们自己写的代码调用Channel的enableReading()函数,就会引发Channel的Update()函数的调用,然后会调用EventLoop的UpdateChannel()函数,实际上会调用到Poller的updateChannel()函数。比如采用EpollPoller,那就会将事件注册到epoll的内核事件表中去,然后开启epoll_wait(),就是我们正常的e'poll流程了。

然后看一下事件发生后处理的时序,先看图:


如图,EventLoop调用poll()是上面事件注册的过程。目前我们处于Poller位置,当Poller中事件发生后,会调用fillActiveChannels()函数,把发生的事件填充给活跃事件通道表中,具体实现是下面的Poller的成员函数fillActiveChannels函数:

void EPollPoller::fillActiveChannels(int numEvents,
                                     ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());//确定它的大小小于events_的大小,因为events_是预留的事件vector                                                                                    
  
  for (int i = 0; i < numEvents; ++i)   //挨个处理发生的numEvents个事件,epoll模式返回的events_数组中都是已经发生额事件,这有别于select和poll
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
/*
这是epoll模式epoll_event事件的数据结构,其中data不仅可以保存fd,也可以保存一个void*类型的指针。
typedef union epoll_data {
               void    *ptr;
               int      fd;
               uint32_t u32;
               uint64_t u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    // Epoll events 
               epoll_data_t data;      //User data variable 
           };
*/
    channel->set_revents(events_[i].events);   //把已发生的事件传给channel,写到通道当中
    activeChannels->push_back(channel);    //并且push_back进activeChannels
  }
}
最后一句push_back不言自明。我们再看EventLoop中核心函数也就是一直循环的loop()函数的处理:

void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();  //断言处于创建该对象的线程中
  looping_ = true;
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
    activeChannels_.clear();  //首先清零
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);  //调用poll返回活动的通道
    ++iteration_; 
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();  //日志登记,日志打印
    }
    // TODO sort channel by priority
    eventHandling_ = true;  //true
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)  //遍历通道来进行处理
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;   //处理完了赋空
    eventHandling_ = false;  //false
    doPendingFunctors();   //
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}
当poll()返回时,loop()函数中会遍历活跃通道表activeChannels,并且执行它们每一个提前注册大的handleEvent()函数,处理完了会继续循环。


整个EpollPoller的作用就是这个样子了,接下来看完全的源码分析:

.h:

#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <vector>

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

#include <muduo/base/Mutex.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/TimerId.h>

namespace muduo
{
namespace net
{

class Channel;
class Poller;
class TimerQueue;

///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
class EventLoop : boost::noncopyable
{
 public:
  typedef boost::function<void()> Functor;

  EventLoop();
  ~EventLoop();  // force out-line dtor, for scoped_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();

  /// Quits loop.
  ///
  /// This is not 100% thread safe, if you call through a raw pointer,
  /// better to call through shared_ptr<EventLoop> for 100% safety.
  void quit();

  ///
  /// Time when poll returns, usually means data arrival.
  ///
  Timestamp pollReturnTime() const { return pollReturnTime_; }

  int64_t iteration() const { return iteration_; }

  /// Runs callback immediately in the loop thread.
  /// It wakes up the loop, and run the cb.
  /// If in the same loop thread, cb is run within the function.
  /// Safe to call from other threads.
  void runInLoop(const Functor& cb);
  /// Queues callback in the loop thread.
  /// Runs after finish pooling.
  /// Safe to call from other threads.
  void queueInLoop(const Functor& cb);

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  void runInLoop(Functor&& cb);
  void queueInLoop(Functor&& cb);
#endif

  // timers

  ///
  /// Runs callback at 'time'.
  /// Safe to call from other threads.
  ///
  TimerId runAt(const Timestamp& time, const TimerCallback& cb);
  ///
  /// Runs callback after @c delay seconds.
  /// Safe to call from other threads.
  ///
  TimerId runAfter(double delay, const TimerCallback& cb);
  ///
  /// Runs callback every @c interval seconds.
  /// Safe to call from other threads.
  ///
  TimerId runEvery(double interval, const TimerCallback& cb);
  ///
  /// Cancels the timer.
  /// Safe to call from other threads.
  ///
  void cancel(TimerId timerId);

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  TimerId runAt(const Timestamp& time, TimerCallback&& cb);
  TimerId runAfter(double delay, TimerCallback&& cb);
  TimerId runEvery(double interval, TimerCallback&& cb);
#endif

  // internal usage
  void wakeup();
  void updateChannel(Channel* channel);  //在poller中注册或者更新通道
  void removeChannel(Channel* channel);  //从poller中移除通道
  bool hasChannel(Channel* channel);

  // pid_t threadId() const { return threadId_; }
  void assertInLoopThread()
  {
    if (!isInLoopThread())  //如果不是,终止程序
    {
      abortNotInLoopThread();
    }
  }
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }  //比较线程id即可。
  // bool callingPendingFunctors() const { return callingPendingFunctors_; }
  bool eventHandling() const { return eventHandling_; }

  void setContext(const boost::any& context)
  { context_ = context; }

  const boost::any& getContext() const
  { return context_; }

  boost::any* getMutableContext()
  { return &context_; }

  static EventLoop* getEventLoopOfCurrentThread();

 private:
  void abortNotInLoopThread();
  void handleRead();  // waked up
  void doPendingFunctors();

  void printActiveChannels() const; // DEBUG

  typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */
  bool quit_; /* atomic and shared between threads, okay on x86, I guess. */
  bool eventHandling_; /* atomic */
  bool callingPendingFunctors_; /* atomic */
  int64_t iteration_;  
  const pid_t threadId_;   //当前所属对象线程id
  Timestamp pollReturnTime_;    //时间戳,poll返回的时间戳
  boost::scoped_ptr<Poller> poller_;  //poller对象
  boost::scoped_ptr<TimerQueue> timerQueue_;
  int wakeupFd_;
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  boost::scoped_ptr<Channel> wakeupChannel_;
  boost::any context_;

  // scratch variables
  ChannelList activeChannels_;   //Poller返回的活动通道,vector<channel*>类型
  Channel* currentActiveChannel_;   //当前正在处理的活动通道

  MutexLock mutex_;
  std::vector<Functor> pendingFunctors_; // @GuardedBy mutex_
};

}
}
#endif  // MUDUO_NET_EVENTLOOP_H
.cpp:

namespace
{
const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;
}

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),   //创建epollfd,使用带1的版本
    events_(kInitEventListSize)    //vector这样用时初始化kInitEventListSize个大小空间
{
  if (epollfd_ < 0)   //在构造函数中判断,<0就abort()
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}

EPollPoller::~EPollPoller()
{
  ::close(epollfd_);   //关闭
}

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_TRACE << "fd total count " << channels_.size();
  int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin(),
                               static_cast<int>(events_.size()),    //使用epoll_wait(),等待事件返回,返回发生的事件数目
                               timeoutMs);  //epoll
  int savedErrno = errno;    //错误号
  Timestamp now(Timestamp::now());  //得到时间戳
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happended";
    fillActiveChannels(numEvents, activeChannels);  //调用fillActiveChannels,传入numEvents也就是发生的事件数目
    if (implicit_cast<size_t>(numEvents) == events_.size())  //如果返回的事件数目等于当前事件数组大小,就分配2倍空间
    {																//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      events_.resize(events_.size()*2);     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    }														       //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  }
  else if (numEvents == 0)  //返回=0
  {
    LOG_TRACE << "nothing happended";
  }
  else
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)   //如果不是EINTR信号,就把错误号保存下来,并且输入到日志中
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;  //返回时间戳
}

//把返回到的这么多个事件添加到activeChannels
/*protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;
*/
void EPollPoller::fillActiveChannels(int numEvents,
                                     ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());//确定它的大小小于events_的大小,因为events_是预留的事件vector
                                                                                       
/*typedef std::vector<struct epoll_event> EventList;   //modify by WY.Huang

  int epollfd_;
  EventList events_;
*/
  
  for (int i = 0; i < numEvents; ++i)   //挨个处理发生的numEvents个事件,epoll模式返回的events_数组中都是已经发生额事件,这有别于select和poll
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
/*
这是epoll模式epoll_event事件的数据结构,其中data不仅可以保存fd,也可以保存一个void*类型的指针。
typedef union epoll_data {
               void    *ptr;
               int      fd;
               uint32_t u32;
               uint64_t u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    // Epoll events 
               epoll_data_t data;      //User data variable 
           };
*/
#ifndef NDEBUG
    int fd = channel->fd();  //debug时做一下检测
    ChannelMap::const_iterator it = channels_.find(fd);
    assert(it != channels_.end());
    assert(it->second == channel);
#endif
    channel->set_revents(events_[i].events);   //把已发生的事件传给channel,写到通道当中
    activeChannels->push_back(channel);    //并且push_back进activeChannels
  }
}

//这个函数被调用是因为channel->enablereading()被调用,再调用channel->update(),再event_loop->updateChannel(),再->epoll或poll的updateChannel被调用
void EPollPoller::updateChannel(Channel* channel)   //更新通道
{
  Poller::assertInLoopThread();
  const int index = channel->index();   //channel是本函数参数,获得channel的index,初始状态index是-1
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted)  //index是在poll中是下标,在epoll中是三种状态,上面有三个常量
  {
    // a new one, add with EPOLL_CTL_ADD
    int fd = channel->fd();
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel;
    }
    else // index == kDeleted
    {
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }

    channel->set_index(kAdded);
    update(EPOLL_CTL_ADD, channel);
  }
  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);  //确保目前它存在
   //
   //poll是把文件描述符变为相反数减1,这里是使用EPOLL_CTL_DEL从内核事件表中删除
   //
    if (channel->isNoneEvent())  //如果什么也没关注,就直接干掉
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted);     //删除之后设为deleted,表示已经删除,只是从内核事件表中删除,在channels_这个通道数组中并没有删除
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);   //有关注,那就只是更新。更新成什么样子channel中会决定。
    }
  }
}

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

//epoll_ctl函数的封装
void EPollPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  bzero(&event, sizeof event);
  event.events = channel->events();
  event.data.ptr = channel;   //传入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)  //使用epoll_ctl
  {
    if (operation == EPOLL_CTL_DEL)  //如果delete失败,不退出
    {
      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";
  }
}



  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值