Muduo學習筆記—Eventloop Channel EPollPoller類

本篇文章在學習完Muduo之後來記錄自己對這幾個類的理解。如有理解不當的地方還請各位朋友指出,感激不盡!

我們先將這三個類的關係理清楚,然後我們再講解源碼的具體實現。

Eventloop類

  • Muduo是one loop per thread 模型。
  • Eventloop類可以看作爲一個指揮家,它不負責具體的事物實現,只負責將這個程序有條不紊的運作起來,Eventloop類不用知道你具體怎麼實現的,只需要知道你這個接口是要做什麼的,我負責將這一類的東西分發到你這裏。

Channel類

  • 通道看名字就有了大概的理解。在日常生活中,通道就是你去做事情的路徑,我去北京當然要做上去北京的高鐵,去西安要坐上去西安的高鐵,你去北京的“通道”是去坐上了西安的高鐵,那你可能這輩子都到不了。
  • Channel類就有了大致的理解,它封裝了網絡編程中的“通道”,網絡編程中需要什麼通道呢?大體就是可讀、可寫、錯誤處理事件,那Channel類就是將這些事情都集於一身形成了一個總通道。

EPollPoller類

  • 該類就是對我們平常使用的epoll進行了封裝。它在Muduo中繼承了Poller類,一會講源碼的時候我們可以看到。

現在知道了各個類的作用,那我們就來看看Muduo中是如何將這三者聯繫起來的。

首先看看EventLoop類的成員

  typedef std::vector<Channel*> ChannelList;

  bool looping_; 
  std::atomic<bool> quit_;
  bool eventHandling_; 
  bool callingPendingFunctors_; 
  int64_t iteration_;
  const pid_t threadId_; //線程ID
  Timestamp pollReturnTime_; //對時間的封裝
  std::unique_ptr<Poller> poller_;  //用unique_ptr是因爲poller是個基類,需要動態綁定
  std::unique_ptr<TimerQueue> timerQueue_; //定時器類
  int wakeupFd_; //喚醒Eventloop所在IO線程的fd
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  std::unique_ptr<Channel> wakeupChannel_; //用於處理wakeupfd_上的readable事
	
  ChannelList activeChannels_; //保存活動的Channel類
  Channel* currentActiveChannel_;	

看看EventLoop::loop()函數,"指揮官"的主邏輯函數

void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread(); //當事件循環時,需要檢查該線程是否爲IO線程
  looping_ = true; //運行時將該成員設爲true
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ? quit函數我們下面會講到
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
    //先將channellist清空
    activeChannels_.clear();
    //這個相當於調用epoll_wait然後將events存入到了activeChannel中。(這篇文章講的是epoll的實現)
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    ++iteration_;
    //日誌我們現在不需要關注它,略過
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    //這裏就是指揮官分發channel,由channel去自己處理自己的事件
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}
void EventLoop::quit()
{
  quit_ = true; //在設置之後loop函數不是立即退出,而是需要在下一次while判斷纔會退出,如果loop阻塞在某個調用,則會導致延遲退出
  // There is a chance that loop() just executes while(!quit_) and exits,
  // then EventLoop destructs, then we are accessing an invalid object.
  // Can be fixed using mutex_ in both places.
  if (!isInLoopThread()) 
  {
    wakeup();
  }
}

看一下EPollPoller的成員
EPollPoller繼承於Poller的,Poller的源碼比較短大家自行去閱讀,我把Poller中的成員放到EpollPoller中來統一說明

  
  typedef std::vector<struct epoll_event> EventList;
  int epollfd_;
  EventList events_ 
  typedef std::map<int, Channel*> ChannelMap; 
  ChannelMap channels_;//用來存放fd->channel的映射關係(每一個fd對於一個自己的channel類)
  EventLoop* ownerLoop_ 
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()),
                               timeoutMs);  //epoll_wait函數,用events_來接收
  int savedErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happened";
    //調用fillActiveChannels函數將收到的活動事件加入到activeChannels中
    fillActiveChannels(numEvents, activeChannels);
    //如果接收到的事件大小和evnts_.size()相等,將其擴容。
    if (implicit_cast<size_t>(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<size_t>(numEvents) <= events_.size());
  for (int i = 0; i < numEvents; ++i)
  {
  	//從events_[i]中取得其對應的channel指針
    Channel* channel = static_cast<Channel*>(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); //將其加入到activeChannels中(activeChannels由前面的EventLoop中傳入)
  }
}

Channel類的成員

  static const int kNoneEvent;
  static const int kReadEvent;
  static const int kWriteEvent;

  EventLoop* loop_;
  const int  fd_;    //保存套接字fd
  int        events_;  
  int        revents_; //epoll返回的該channel類對應事件的events(用來判斷其可讀可寫還是錯誤連接)
  int        index_; // used by Poller. pooler中poolfd的數組下標,爲了方便找尋該channel的位置,在epoll中則不需要該成員,因爲epoll底層用紅黑數實現,不需要保存其下標。
  bool       logHup_;

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;
 
  //通道的不同回調函數(就是我們前面說明的不同類型的事件調用不同類型的函數)
  ReadEventCallback readCallback_;  
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

Channel類中和EventLoop有關係的成員函數

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}
//來判斷事件的類型來執行所對應的回調函數
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)
  {
    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;
}

總結

看到這裏我們來總結一下三個類的關係。

  1. EventLoop::loop()中調用EPollPoller::poll()來獲得活動的Channel集合
  2. EventLoop::loop()再將獲得的Channel集合進行分發
  3. 分發完的Channel執行自己的handleEvent函數
  4. handleEvent函數在去判斷對應的事件去分發回調函數

至此我們將三個類的整體流程進行了講解和分析,詳細的內容還是要自己去看Muduo源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章