muduo中的reactor

muduo使用的是reactor模式,关于muduo的其他内容不做过多赘述。此文作为自己阅读muduo源码的笔记,目的在于用直白的语言记录阅读时的理解。
muduo网络库,分为两个部分。base模块主要为实现网络库的一些基础工具,如互斥锁、条件变量、日志库、线程池等等。net模块即是reactor模式的网络通信主要实现部分。

1. net

个人阅读开源代码喜欢从它的使用方法示例看起。先从最顶层了解库的使用方式,再自顶向下逐渐深入,类似于广度优先遍历的方式。然后在感兴趣的部分有选择性的继续深入。
先看一个《Linux多线程服务器端编程》中的例子:

#include 
muduo::EventLoop* g_loop;
void  timeout()
{
  printf("Timeout!\n");
  g_loop->quit();
}

int main()
{
  muduo::EventLoop loop;
  g_loop = &loop;
  int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
  muduo::Channel channel(&loop, timerfd);
  channel.setReadCallback(timeout);
  channel.enableReading();
  struct itimerspec howlong;
  bzero(&howlong, sizeof howlong);
  howlong.it_value.tv)sec = 5;
  ::timerfd_settinme(timerfd , 0, &howlong, NULL);
  loop.loop();
  ::close(timefd);
}

上段代码,出现了两个重要的类EventLoopChannel,Channel的构造函数使用了EventLoop,并且channel对象可以设置回调函数(setReadCallback()),以及设置关心的回调事件(enableReading())。最后调用loop.loop()开启监听。

1.1 EventLoop & Channel & Poller

EventLoop理解为Poller运转的引擎。
Poller实现了poll或者epoll,用来监听Channel。
Channel对应描述符,绑定了描述符事件的处理方法。

先看一下EventLoop的数据成员

private:
  bool looping_; /* atomic */
  std::atomic quit_;
  bool eventHandling_; /* atomic */
  bool callingPendingFunctors_; /* atomic */
  int64_t iteration_;
  const pid_t threadId_;
  Timestamp pollReturnTime_;
  std::unique_ptr poller_;
  std::unique_ptr timerQueue_;
  int wakeupFd_;
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  std::unique_ptr wakeupChannel_;
  boost::any context_;

  // scratch variables
  ChannelList activeChannels_;
  Channel* currentActiveChannel_;

  mutable MutexLock mutex_;
  std::vector pendingFunctors_ GUARDED_BY(mutex_);

可以看到其中有Channel对象的列表activeChannels。这个列表的内容是如何填充的呢?在代码中搜索,这个列表,发现在EventLooop的loop函数定义中对activeChannels进行了修改。

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_); //此处通过poller对象填充channels列表。
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}

这里涉及了一个新的poller对象。通过poller对象的poll函数填充了channels列表,并以此调用了列表中channel的handleEvent方法。由此大概可以猜测出,channel由poller管理,poller循环取出活动状态的channle,并触发其handleEvent方法。
接下来分析channel是如何与poller绑定的。
查看poller的接口:

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

  Poller(EventLoop* loop);
  virtual ~Poller();

  /// Polls the I/O events.
  /// Must be called in the loop thread.
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;

  /// Changes the interested I/O events.
  /// Must be called in the loop thread.
  virtual void updateChannel(Channel* channel) = 0;

  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  virtual void removeChannel(Channel* channel) = 0;

  virtual bool hasChannel(Channel* channel) const;

  static Poller* newDefaultPoller(EventLoop* loop);

  void assertInLoopThread() const
  {
    ownerLoop_->assertInLoopThread();
  }
...
}

可以看出,poller实际上是一个虚基类,在muduo中有有两个poller的具体实现,EPollPoller与PollPoller。跳转到EPollPoller::updateChannel()

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)
  {
    // 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);
    if (channel->isNoneEvent())
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted);
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);
    }
  }
}

可以看出,poller对象维护了一个channels[]数组,通过updateChannel向其中添加channel。通过查找该方法的引用,跳转到EventLoop::updateChannel();

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

继续查找 EventLoop::updateChannel(Channel* channel)的引用。跳转到void Channel::update();

void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);
}

再查找Channel::update()的引用,跳转到void enableReading()

void enableReading() { events_ |= kReadEvent; update(); }

通过以上分析,基本上清楚了三个类之间的关系以及作用。
Poller中绑定了关心的Channle,并监听Channel的活动。
EventLoop发起循环,使得Poller循环输出active的Channel,并调用Channel的回调函数。

你可能感兴趣的:(muduo中的reactor)