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源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值