Muduo:EventLoop“循环”什么?

涉及的类:EPollPoller EventLoop Channel TimeQueue
其中错误,欢迎指出!

  • 关系
    EventLoop类会指派其成员变量poller_监听3种类型的事件,一种是由Channel类描述的读写事件,一种是TimeQueue类描述的定时事件(然而也是通过Channel来注册到poller上),还有一种则是自身线程唤醒事件。
    你问EPollPoller类是干啥的?poller_变量则是由EPollPoller类描述的。
    这个EventLoop可以说是一个指挥官,具体的细节他不知道,它只知道你这个类会干这事,所以代码中你会见到EventLoop很喜欢这么干,你这个成员变量,去你的类中用你实现的那个方法。你说让poller_监听事件吧,好的。让poller_自个去调用它类中实现的poll方法。这里面有很多代码,下面会提到。
    监听到了会干嘛呢?没错,EventLoop也不知道监听了要干嘛,因为这事情他不爱记,他只记录了监听到的事件activeChannels_,而这些事件是由Channel类描述的,那里会记载着该干嘛。EventLoop就会调用其中设置的回调函数。
//EventLoop.h
boost::scoped_ptr poller_;
ChannelList activeChannels_;  //监听到的事件

首先先了解EPollPoller与Channel,这两个类是有联系的,看代码的时候建议一起看。
- EPollPoller与Channel
首先EPollPoller类是继承了Poller类的,由于Poller类实在太短,所以我就拿了EPollPoller类说。
(epoll:http://blog.csdn.net/ljx0305/article/details/4065058)
成员变量如下:

  int epollfd_;   //epoll句柄,以后要监听什么事件注册到这里        
  typedef std::vector<struct epoll_event> EventList;
  EventList events_;   //监听到的活动的事件,作为epoll_wait参数
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_; //描述符与Channel(事件)的映射,这里的事件,不一定是正在监听的事件(因为有可能是之前监听但后来删掉不过仍留在ChannelMap中的映射)
  EventLoop* ownerLoop_;//指向EventLoop的指针。

还是直接在代码中注释好了。上面是介绍了EPollPoller类的成员变量,以下是成员方法的注释。

#include 
#include 
#include 
#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.
BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);
BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);
BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);
BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);
BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);
BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);
namespace
{
/*
这3个const变量是来设置Channel类的index变量。
用来指示Channel事件在EPollPoller的注册情况。
具体分析见该段代码下的列表中。。
*/
const int kNew = -1;    
const int kAdded = 1;
const int kDeleted = 2;
}
//构造函数:调用epoll_create1创建了epoll句柄。初始化了其他成员变量。
EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
    events_(kInitEventListSize)
{
  if (epollfd_ < 0)
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}
//析构函数:关闭了epollfd
EPollPoller::~EPollPoller()
{
  ::close(epollfd_);
}
/***poll函数:
poll监听在epollfd上注册的描述符,其中activeChannels变量是EventLoop中传递过来的
(EventLoop类只记载了活跃的事件,注册了什么事件交给了其成员变量poller),通过
fillActiveChannels函数来完成写入到activeChannels。
***/
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_TRACE << "fd total count " << channels_.size();
//调用epoll_wait,活跃的事件则放在events中。
  int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin(),
                               static_cast<int>(events_.size()),
                               timeoutMs);
  //保存errno     
  int savedErrno = errno;
//这里层层调用下去会发现其实就是调用了gettimeofday记录了下时间。
  Timestamp now(Timestamp::now());
//对epoll_wait返回值判断。
  if (numEvents > 0)//有活跃事件
  {
    LOG_TRACE << numEvents << " events happended";
  /*
  将记录了活跃事件成员events塞到activeChannels中,因为poll函数主要
  由EventLoop类调用,传递参数,activeChannels是EventLoop类的成员变量。
  */
    fillActiveChannels(numEvents, activeChannels); 
    if (implicit_cast(numEvents) == events_.size())
    {
      events_.resize(events_.size()*2);
    }
  }
  else if (numEvents == 0) //无活跃事件
  {
    LOG_TRACE << "nothing happended";
  }
  else  //出错
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;
}
/**fillActiveChannels
创建一个临时channel,用events来设置其中的变量,尤其是是fd,revents,具体与events中变
量的对应见下。应该是类Channel与结构体struct epoll_event events的对应。创建好再将其
push_back到activeChannels中。
**/
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);
#ifndef NDEBUG
    int 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);
    activeChannels->push_back(channel);
  }
}
//注册或修改channel。对照kNew kDeleted kAdded来食用更佳
void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  const int index = channel->index();
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted) //说明该channel未注册到epollfd中
  {
    // a new one, add with EPOLL_CTL_ADD
    int fd = channel->fd();
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel; //kNew表示std::map channels_也没有,添加进去。
    }
    else // index == kDeleted
    {
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }

    channel->set_index(kAdded);
    update(EPOLL_CTL_ADD, channel); //注册到epollfd
  }
  else   //已注册 kAdded。
  {
    // 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())   //不监听事件了
    {
      update(EPOLL_CTL_DEL, channel);//从epollfd中删除
      channel->set_index(kDeleted);
    }
    else  //已注册的channel,也许是更改了一些监听事件类型
    {
      update(EPOLL_CTL_MOD, channel);//修改该事件。
    }
  }
}
//从epollfd和std::map中都删除channel,代码很简单。喜欢用assert来检查
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();//这两行代码也能反应channel跟struct epoll_event的映射。
  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)
  {
    if (operation == EPOLL_CTL_DEL)
    {
      LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
    else
    {
      LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
  }
}
//这个是转出string,没什么好说的。
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";
  }
}
  • kNew kDeleted kAdder
某个channel的index变量 EPollPoler类的ChannelMap 是否注册了epollfd_
kNew 未存有 未监听
kDeleted 存有 未监听
kAdded 存有 已监听
  • struct epoll_event与Channel的对应。
typedef union epoll_data {
    void *ptr;   //指向channel类变量
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event {
    __uint32_t events; //对应channel类中的revent变量
    epoll_data_t data; //data.ptr
};

对应的代码:

//在fillActiveChannels函数中的代码体现
 Channel* channel = static_cast(events_[i].data.ptr);
 channel->set_revents(events_[i].events);

//在update函数中的代码体现
  event.events = channel->events();
  event.data.ptr = channel;

EPollPoller类和Channel类的交互。是通过Channel类中的index和revent进行交互的。index用来告知EPollPoller注册情况。revent告知Channel啥类型的事件活跃。
Channel类的成员变量

 EventLoop* loop_;
  const int  fd_;   //描述符
  int        events_; //监听的事件类型
  int        revents_; // it's the received event types of epoll or poll
  int        index_; // used by Poller.
  bool       logHup_;

  boost::weak_ptr<void> tie_; //锁?
  bool tied_;
  bool eventHandling_;  //是否正在执行回调函数?
  bool addedToLoop_;   //是否加入到时间循环?
  typedef boost::function<void()> EventCallback;
  typedef boost::function<void(Timestamp)> ReadEventCallback;

  ReadEventCallback readCallback_;//读回调
  EventCallback writeCallback_; //写回调
  EventCallback closeCallback_;//关闭回调
  EventCallback errorCallback_;//出错回调

然后是介绍成员方法:

//先介绍代码少的内联函数。在channel.h头文件中。
 int fd() const { return fd_; }
  int events() const { return events_; }
  void set_revents(int revt) { revents_ = revt; } // used by pollers
  // int revents() const { return revents_; }
  bool isNoneEvent() const { return events_ == kNoneEvent; }
//关于kReadEvent|kWriteEvent|kNoneEvent,见channel.cc文件。下面也会列出。
//update函数其实层层深入会发现其实就是调用了EPollPoller类的updateChannel方法。注册或修改。
  void enableReading() { events_ |= kReadEvent; update(); }
  void disableReading() { events_ &= ~kReadEvent; update(); }
  void enableWriting() { events_ |= kWriteEvent; update(); }
  void disableWriting() { events_ &= ~kWriteEvent; update(); }
  void disableAll() { events_ = kNoneEvent; update(); }
  bool isWriting() const { return events_ & kWriteEvent; }
  bool isReading() const { return events_ & kReadEvent; }

  // for Poller
  int index() { return index_; }
  void set_index(int idx) { index_ = idx; }

//下面这个是channel.cc文件上的。


#include 
#include 
#include 

#include 

#include 

using namespace muduo;
using namespace muduo::net;

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
//构造函数:初始化变量,index赋值为-1,即kNew,表示还未加入epollfd和std::map channels_。
Channel::Channel(EventLoop* loop, int fd__)
  : loop_(loop),
    fd_(fd__),
    events_(0),
    revents_(0),
    index_(-1),
    logHup_(true),
    tied_(false),
    eventHandling_(false),
    addedToLoop_(false)
{
}

Channel::~Channel()
{
  assert(!eventHandling_);
  assert(!addedToLoop_);
  if (loop_->isInLoopThread())
  {
    assert(!loop_->hasChannel(this));
  }
}

void Channel::tie(const boost::shared_ptr<void>& obj)
{
  tie_ = obj;     //对锁的赋值
  tied_ = true;    //有锁标志
}
//这里update调用了EventLoop中的updateChannel,其实追根究底还是在调用EPollPoller中updateChannel方法。
void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);
}
//与update同理,层层调用去看还是在调用EPollPoller中removeChannel方法
void Channel::remove()
{
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);
}
/*
handleEvent:
作用:供EventLoop类调用,因为EventLoop类只会发现你这个channel有事件
    ,至于是什么事件需要channel类自己查看revent变量(EPollPoller传递的),
    然后根据其值来调用相关的事件回调函数。(这也是handleEventWithGuard的逻辑)
逻辑:判断这个channel是否有锁,一般该事件涉及多事件调用才需要用到锁。
若有锁,则获得锁后调用handleEventWithGuard
若无锁,直接调用handleEventWithGuard。
*/
void Channel::handleEvent(Timestamp receiveTime)
{
  boost::shared_ptr<void> guard;
  if (tied_)  //判断是否有锁。有锁。
  {
    guard = tie_.lock();//锁上!
    if (guard)  //是否锁上了。
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}
/*
handleEventWithGuard:
根据revent值来调用相关的事件回调函数。
man 2 poll 可见每个宏定义POLLXXX的含义。
*/
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
  {
    if (logHup_)
    {
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
    }
    if (closeCallback_) closeCallback_();
  }

  if (revents_ & POLLNVAL) //fd未打开。。。见man 2 poll
  {
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }

  if (revents_ & (POLLERR | POLLNVAL))   //出错
  {
    if (errorCallback_) errorCallback_();
  }
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))    //收到
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)  //写
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}
//event事件类型转字符串
string Channel::reventsToString() const
{
  return eventsToString(fd_, revents_);
}

string Channel::eventsToString() const
{
  return eventsToString(fd_, events_);
}
//
string Channel::eventsToString(int fd, int ev)
{
  std::ostringstream oss;
  oss << fd << ": ";
  if (ev & POLLIN)
    oss << "IN ";
  if (ev & POLLPRI)
    oss << "PRI ";
  if (ev & POLLOUT)
    oss << "OUT ";
  if (ev & POLLHUP)
    oss << "HUP ";
  if (ev & POLLRDHUP)
    oss << "RDHUP ";
  if (ev & POLLERR)
    oss << "ERR ";
  if (ev & POLLNVAL)
    oss << "NVAL ";

  return oss.str().c_str();
}

Channel跟EPollPoller逻辑一般是这样:
创建个channel,比如设置读事件,先设置回调函数,然后enableReading(),这里就非常关键了,它会先设置events变量的事件类型,然后update,这个update会让该事件所在loop来调用loop上的EPollPoller的updateChannel。具体代码如下:

//Channel.cc
void Channel::update()
{
  addedToLoop_ = true;

  loop_->updateChannel(this);   

}
//EventLoop.cc
void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();

  poller_->updateChannel(channel);

}

以上是设置事件并注册到epollfd中的过程。删除事件,以及channel.h中的disableXXX同理。
而监听到事件的代码也是非常清晰的。首先在EventLoop的loop函数中监听到。然后装进activeChannels_,再由channel类调用本身的handleEvent。

//EventLoop.cc
loop(……){
……
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
……
for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
……
}

Channel一般为读写事件。

1、一般有这个场景,当前有个EventLoop loopA在时间循环中。
2、我们这时候有个读事件想在loopA中监听,好,先创建一个channel,弄好描述符,还有loop_也指向loopA,设置好感兴趣的事件类型,还有回调函数。其中index为kNew,用来等会告诉EPollPoller说,这个channel注册情况是一片空白,既不在epollfd上监听,也没再那个map上。然后enableXXX,里面就会update一下,调用了loopA中的updatechannel方法。
3、loopA一看,什么?要我帮你update,这里可能是注册,也可能是del啥的,具体干嘛他不知道,但他知道他的手下—-成员变量poller_是干这类事情的,他会吩咐poller说,这事情交给你去办。而channel交给loopA时候把“登记材料”(index、events)也准备好了,所以在epollfd上登记这个读事件就会在EPollPoller上实际进行。
4、登记完后,过了会,读事件来了,这时候最先知道这件事情的是epollfd,于是他把这事情告诉了loopA手下activeChannels,让他转告loopA,让他拿主意,loopA听到后,就吩咐这事件所属的channel说,你们的读事件来了,快去解决一下。
5、然后channel就检测revent,是epollfd上检测出的事件的类型,然后再调用相应的读或写或出错回调函数。

——————————————————————————————————————————————————————
写到这里,有点倦了
就把整个大致说一遍,以后要是忘了还能找回其中的逻辑。真要写完写清楚对每条代码都解释清楚,要写好久。
1、定时事件muduo也是使用了描述符,用TimerQueue类来描述,在构造这个定时事件的时候封装成channel,让poll来监听其读事件。(对TimerQueue还不算太清楚)
2、EventLoop自身有个唤醒自身线程的描述符,在创建的时候就注册到了epollfd上。

void EventLoop::runInLoop(const Functor& cb)
{
  if (isInLoopThread())
  {
    cb();
  }
  else
  {
    queueInLoop(cb);
  }
}

void EventLoop::queueInLoop(const Functor& cb)
{
  {
  MutexLockGuard lock(mutex_);
  pendingFunctors_.push_back(cb);
  }

  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();//唤醒。往唤醒描述符上写……
  }
}

runInLoop函数会催着loop所在线程去执行cb函数。
3、还有一类事件就是channel。在上面说的也算详细了。

你可能感兴趣的:(muduo)