muduo net库学习笔记2——muduo网络库相关类图的关系、EventLoop、Channel、 Poller

EventLoop的简化封装

(有在上篇文章中出现过但是连贯一下就还是搬过来…,看完类图关系就可以分析完善的代码了
可以直接跳转到正文类图关系

.h文件

namespace muduo
{
namespace net
{
///
/// Reactor, at most one per thread.  EventLoop就是Recator模式的封装,每个线程最多一个
///
/// This is an interface class, so don't expose too much details.
class EventLoop : noncopyable
{
 public:
  typedef std::function<void()> Functor;

  EventLoop();
  ~EventLoop();  // force out-line dtor, for std::unique_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();//事件循环函数,该函数不能跨线程调用
 void assertInLoopThread()
  {
    if (!isInLoopThread())
    {
      abortNotInLoopThread();
    }
  }
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
private:
  void abortNotInLoopThread();
  static EventLoop* getEventLoopOfCurrentThread();
  bool looping_; /* atomic */ //是否处于循环的状态
  const pid_t threadId_;//记录当前对象属于哪一个线程,当前对象所属线程id
};
}
}
#endif //MUDUO_NET_EVENTLOOP_H

.cc文件
每一个线程最多有一个EventLoop对象,.cc文件第一行代码就定义一个线程局部存储的当前线程EventLoop对象指针__thread EventLoop* t_loopInThisThread = 0;,再提一下,因为有__thread这个关键字就表示每一个线程都有这样的一个变量,否则的话这个变量是共享的
创建对象成功的话,这个指针t_loopInThisThread就指向当前对象,当然这个对象得是第一次创建,因为对象最多只能有一个。
报错都用日志的记录方式

构造函数
初始化looping为false,表示当前还没有处于循环的状态,并把当前线程的真实id初始化给ThreadId_

EventLoop::EventLoop():looping(false),thraedId_(CurrentThread::tid())
{
	LOG_TRACE << "EventLoop create "<<this<<"in thread "<<threadId_;//日志的记录
	 if (t_loopInThisThread)//检查当前线程是否已经创建了其他EventLoop对象,如果已经创建了则LOG_FATAL终止程序
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this;//既然创建了一个对象,那么当前的对象指针就指向this
  }
}

**析构函数:**指向对象的指针置空

EventLoop::~EventLoop()
{
  LOG_DEBUG << "EventLoop " << this << " of thread " << threadId_
            << " destructs in thread " << CurrentThread::tid();
  t_loopInThisThread = NULL;
}

loop函数:事件循环
事件循环,该函数不能跨线程调用,只能在创建该对象的线程中调用

void EventLoop::loop()
{
  assert(!looping_);//断言还没有开始循环
  assertInLoopThread();//断言当前处于创建该对象的线程中,函数的实现
  looping_ = true;//开始要事件循环了,先将looping_置为true
  LOG_TRACE << "EventLoop " << this << " start looping";//打印事件

  ::poll(NULL, 0, 5*1000);//NULL表示当前没有关注事件,关注事件个数为0,等待5秒,仅仅而已,走个场
  
  LOG_TRACE << "EventLoop " << this << " stop looping";//loop完也打印一下
  looping_ = false;//并置回false
}

assertInLoopThread()判断是都处于创建该对象的线程中

 void assertInLoopThread()
  {
    if (!isInLoopThread())//只需要将当前线程的id与threadId比较是否相等
    {
      abortNotInLoopThread();//阻止程序
    }
  }

bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }

void EventLoop::abortNotInLoopThread()
{
  LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
            << " was created in threadId_ = " << threadId_
            << ", current thread id = " <<  CurrentThread::tid();
}//这就是日志的用法啦,开心

muduo类图关系

muduo网络库相关类图的关系其实就是muduo库Reactor模式的实现:

Reactor模式简介: Reactor是一种事件驱动机制。它和普通函数调用的不同之处在于:应用程序不是主动调用某个API完成处理,恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”

类图如下
muduo net库学习笔记2——muduo网络库相关类图的关系、EventLoop、Channel、 Poller_第1张图片
muduo中Reactor的关键结构包括:EventLoopPollerChannel
如类图所示,EventLoop类和Poller类属于组合的关系,EventLoop类和Channel类属于聚合的关系, 这两种关系都是关联关系的特例
聚合关系体现的是整体与部分的关系,整体与部分是可分离的,具有各自的生命周期,EventLoop类不负责Channel类的生命周期,由图可知,Channel的生命周期是由TcpConnection类,Acceptor类,Connector类控制(空心菱形)
组合关系比聚合关系强,也称为强聚合,虽然也是整体和部分的关系,但是这个整体和部分是不能分割的,整体的生命周期结束意味着部分的生命周期也结束了(实心菱形)

另外:类图中白色部分类EventLoopTcpConnectionTcpServerTcpClient都是对外公开的,是外部类;其他灰色部分表示内部类,对外是不可见的

先详细讲讲这些类是干嘛的

EventLoop类是对事件循环的抽象
Poller类是对IO复用的抽象,是muduo库中唯一用面向对象实现的,Poller是一个抽象类,继承有PollPollerEPollPoller两个子类,但这三个都是内部类,虽然用面向对象的方法实现了,但还是不用虚函数来暴露接口
Channel类是对IO事件的注册与相应的封装,有一个Update函数负责IO事件的可读可写事件,handleEvent事件负责对锁发生的IO事件进行处理
Acceptor类是被动连接的抽象,关注的是套接字的可读事件,Connector类是对主动连接的抽象,但不管是主动还是被动,连接建立起来了就会有一个已连接套接字,那么对已连接套接字的抽象就是 TcpConnection类;TcpServer包含一个AcceptorTcpClient包含一个Connector

一个EventLoop包含多个Channel, 即它可以捕捉多个通道的可读可写事件;
Channel不拥有文件描述符,即Channel对象被销毁的时候不关闭文件描述符,不会调用close()去关闭文件描述符;
Channel跟文件描述符FileDescripto是关联关系,一个Channel一个fd,
一个EventLoop多个fd,监听多个fd的相关事件;
fd是被套接字Socket拥有的,即fd生存期由套接字控制,当套接字对象被销毁的时候文件描述符就会被套接字类的析构函数调用close()销毁;

另外⭐:从类及其函数我们发现:
当我们调用Channel中的Update来注册或者更新IO的读和写事件的时候,会调用到EventLoop的updateChannel函数,从而又调用到Poller的updateChannel函数,这相当于就是将Channel注册到Poller当中(上篇文章的muduo的TCP网络本质

Acceptor接收到客户端请求,调用TcpServer回调函数
TcpServer回调函数中创建TcpConnection对象,代表着一个Tcp连接
TcpConnection构造函数中创建Channel对象,保存客户端套接字fd和关心的事件(可读)
Channel 注册自己到所属事件驱动循环(EventLoop)中的Poller上
Poller开始监听,当发现Channel被激活后将其添加到EventLoop的激活队列中
EventLoop在poll返回后处理激活队列中的Channel,调用其处理函数
Channel在处理函数中根据被激活的原因调用不同的回调函数(可读/可写

用时序图来根据事件的循环流程来理解它们的实现

muduo net库学习笔记2——muduo网络库相关类图的关系、EventLoop、Channel、 Poller_第2张图片
EventLoop::loop()调用Poller::poll()获得当前活动事件的Channel列表,再遍历该列表,执行每个Channel的Channel::handleEvent()完成相应就绪事件回调。

Poll是一个抽象类,所以可能调用pollPoller的poll,也可能调用epollPoller的poll, poll()之后返回了一些通道channel,检测到一些通道的文件描述产生的事件返回activeChannels, 调用这些channel的handleEvent()就会调用回调函数,5,6是表示有可能不止一个通道调用不同的回调函数, 再次poll

代码分析 (根据类图与时序图

在简化代码上增加类图上的点:先前向声明两个类,class Channelclass Poller;增加数据成员:首先多了一个Poller类对象scoped_ptr poller_Timestamp pollReturnTime_调用poller函数时返回的时间戳、activeChannels_poller函数返回的活动通道、currentActiveChannel_当前正在处理的活动通道、对应looping_是否处于循环的状态有quit_用于标识是否退出、eventHandling当前是否处于事件处理的状态
还多了两个成员函数updateChannel()removeChannel()
EventLoop.h

namespace muduo
{
namespace net
{
class Channel;
class Poller;
///
/// Reactor, at most one per thread.  EventLoop就是Recator模式的封装,每个线程最多一个
///
/// This is an interface class, so don't expose too much details.
class EventLoop : noncopyable
{
 public:
  typedef std::function<void()> Functor;

  EventLoop();
  ~EventLoop();  // force out-line dtor, for std::unique_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();//事件循环函数,该函数不能跨线程调用
  void quit();
///
/// Time when poll returns, usually means data arrival
///
Timestamp pollReturnTime() const {return pollReturnTime_;}

//inter usage
void updateChannel(Channel* channel);//在Poller中添加或更新通道
void removeChannel(Channel* channel);//从Poller中移除通道
 void assertInLoopThread()
  {
    if (!isInLoopThread())
    {
      abortNotInLoopThread();
    }
  }
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
  static EventLoop* getEventLoopOfCurrentThread();
  
private:
  void abortNotInLoopThread();
  void printActiveChannels() const;//DEBUG
  typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */ //是否处于循环的状态
  bool quit_; /* atomic */ //是否退出
  bool eventHandling_; /* atomic */ //当前是否处于事件处理的状态
  const pid_t threadId_;//记录当前对象属于哪一个线程,当前对象所属线程id
  Timestamp pollReturnTime_; //调用poll函数的时候所返回的时间戳
  boost::scoped_ptr<Poller> poller_;// 包含了一个pooler_对象 scoped_ptr类型,也表明poller的生存周期由EventLoop控制
  ChannelList activeChannels_;//Poller返回的活动通道,即产生了事件的一些通道
  Channel* currentActiveChannel_;//当前正在处理的活动通道
};
}
}
#endif //MUDUO_NET_EVENTLOOP_H

loop.cc文件 (还不是完善的代码
首先将活动通道清除,然后调用poll来返回活动通道,遍历活动通道,每一个正在遍历的为当前活动通道,调用handleEvent处理这个通道,遍历结束更新标识返回到循环,继续poll

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;//更新这个标识
    for (Channel* channel : activeChannels_)//遍历这些活动通道进行处理
    {
      currentActiveChannel_ = channel;//遍历每一个都作为当前的正在活动通道
      currentActiveChannel_->handleEvent(pollReturnTime_);//调用handleEvent处理这个通道
    }
    currentActiveChannel_ = NULL;//一旦处理完,正在处理的活动通道就为空
    eventHandling_ = false;//更新标识,又回到调用poll
    //doPendingFunctors();
  }

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

通道类 Channel.h

成员函数主要是:
1,设置回调函数set*callback
2,设置对fd关心的事件,将自己存储的fd及相应的事件注册到Poller中enable*
3,删除对fd的监听,将其从pollerchannelMap中移除disableAll
4,被激活时调用的回调函数handleEvent
∴Channel就是一个事件类,保存fd和需要监听的事件,以及各种回调函数

class EventLoop;
///A selectable I/O channel. This class doesn't own the file descriptor. 这个类不拥有一个文件描述符 The file descriptor could be a socket, an eventfd, a timerfd, or a signalfd
class Channel : noncopyable
{
 public:
  typedef std::function<void()> EventCallback;//事件的回调处理
  typedef std::function<void(Timestamp)> ReadEventCallback;//读回调处理,需要多传一个时间戳
  /*
  function,可调用函数对象,类似函数指针
  模板参数是函数标签
  */

  Channel(EventLoop* loop, int fd);//构造函数,传递一个Eventloop进来,一个EventLoop可以有多个channel,但一个channel只能在一个EventLoop中处理
  ~Channel();

  void handleEvent(Timestamp receiveTime);//当事件到来会调用handleEvent来进行处理,即被激活时调用的函数,内部调用下面设置的几个函数
 
  void setReadCallback(ReadEventCallback cb)//读的回调函数
  { readCallback_ = std::move(cb); }
  void setWriteCallback(EventCallback cb)//写的回调函数
  { writeCallback_ = std::move(cb); }
  void setCloseCallback(EventCallback cb)//关闭的回调函数
  { closeCallback_ = std::move(cb); }
  void setErrorCallback(EventCallback cb)//错误回调
  { errorCallback_ = std::move(cb); }

  /// Tie this channel to the owner object managed by shared_ptr,
  /// prevent the owner object being destroyed in handleEvent.
  void tie(const std::shared_ptr<void>&);//用于保存TcpConnection指针

  int fd() const { return fd_; }//channel所对应的文件描述符
  int events() const { return events_; }//channel注册的事件保存在events中
  void set_revents(int revt) { revents_ = revt; } // used by pollers
  // int revents() const { return revents_; }
  bool isNoneEvent() const { return events_ == kNoneEvent; }

/* 下边的Channel的update(),实际是调用EventLoop的updateChannel,
void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);
}
把通道注册进来,从而注册到EventLoop所持有的Poller对象中
void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}
能不能将注册理解为:A注册到B,A做的事其实是B在做
 */
//将自己注册到Poller中,或从Poller中移除,或只删除某个事件,update实现 
  void enableReading() { events_ |= kReadEvent; update(); }
  void disableReading() { events_ &= ~kReadEvent; update(); }
  void enableWriting() { events_ |= kWriteEvent; update(); }
  void disableWriting() { events_ &= ~kWriteEvent; update(); }//disable不关注事件了
  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; }

  // for debug
  string reventsToString() const;//将事件转换成字符串以便调试
  string eventsToString() const;

  void doNotLogHup() { logHup_ = false; }

  EventLoop* ownerLoop() { return loop_; }
  void remove();//调用remove之前确保调用disableAll

 private:
  static string eventsToString(int fd, int ev);
  

  void update();
  void handleEventWithGuard(Timestamp receiveTime);//handleEvent调用这个函数

  static const int kNoneEvent;//常量,没有事件
  static const int kReadEvent;//POLLIN or POLLPRI(紧急数据)
  static const int kWriteEvent;//pollout

  EventLoop* loop_; //所属EventLoop
  const int  fd_; //文件描述符,但不负责关闭该文件描述符
  int        events_; //关注的事件  注册的事件
  int        revents_; // it's the received event types of epoll or poll 是poll/epoll返回的事件  实际返回的事件
  int        index_; // used by Poller. 标识在poll的事件数组中的序号
  bool       logHup_;// for POLLHUP

/* 
   * tie_存储的是TcpConnection类型的指针,即TcpConnectionPtr
   * 一个TcpConnection代表一个已经建立好的Tcp连接
   */
  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;//是否处于事件中
  bool addedToLoop_;
  ReadEventCallback readCallback_;//读事件的回调
  EventCallback writeCallback_;//回调
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_CHANNEL_H

通道类 Channel.cc

// Copyright 2010...略

#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"

#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;

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 std::shared_ptr<void>& obj)/
{
  tie_ = obj;
  tied_ = true;
}

void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);//实际调用EventLoop的函数,注册进EventLoop内
}

void Channel::remove()
{
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);//调用removeChannel
}

void Channel::handleEvent(Timestamp receiveTime)//当事件到来,调用handelEvent进行处理,根据fd的激活事件的不同,调用不同的fd回调函数
{
/*
RAII,对象管理资源
配合只能指针⭐
如果想要判断Channel所在的TcpConnection是否仍然存在(没有被关闭),可以直接将weak_ptr提升为shared_ptr,如果提升成功(不为NULL),代表TcpConnection存在,调用回调函数即可。
提升成shared_ptr后TcpConnection引用计数变为2,handleEvent函数返回后局部变量guard销毁,引用计数恢复到1。
同样的应用还有锁对象,创建时上锁,析构时解锁
*/
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}

//⭐Channel::handleEvent()调用Channel::handleEventWithGuard()处理事件。
/*其实就是根据不同的激活原因调用不同的回调函数,这些回调函数都在TcpConnection中*/
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))  // POLLHUP:发生挂起
  {
    if (logHup_)
    {
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";//打日志
    }
    if (closeCallback_) closeCallback_();
  }

  if (revents_ & POLLNVAL)  // POLLNVAL:描述符不是一个打开的文件
  {
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }

  if (revents_ & (POLLERR | POLLNVAL))  // POLLERR:发生错误
  {
    if (errorCallback_) errorCallback_();
  }
  // POLLIN:普通或优先级带数据可读
  // POLLPRI:高优先级数据可读
  // POLLRDHUP:优先级带数据可读
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
  {
    if (readCallback_) readCallback_(receiveTime);
  }
    // POLLOUT:普通数据可写
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}

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

Poller类

Poller 类是IO多路复用的封装,在muduo中它是一个抽象基类,因为muduo同时支持poll和epoll两种IO多路复用机制。
一个EventLoop包含一个Poller, 所以Poller也会登记它所属的EventLoop

// Copyright 2010, Shuo Chen.  All rights reserved.// http://code.google.com/p/muduo///// Use of this source code is governed by a BSD-style license// that can be found in the License file.// Author: Shuo Chen (chenshuo at chenshuo dot com)//// This is an internal header file, you should not include this.

#ifndef MUDUO_NET_POLLER_H
#define MUDUO_NET_POLLER_H

#include 
#include 

#include "muduo/base/Timestamp.h"
#include "muduo/net/EventLoop.h"

namespace muduo
{
namespace net
{

class Channel;

///
/// Base class for IO Multiplexing
///
/// This class doesn't own the Channel objects.
class Poller : noncopyable
{
 public:
  typedef std::vector<Channel*> 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();
  }

 protected:
  typedef std::map<int, Channel*> ChannelMap;//返回的通道列表存储在map中,key是文件描述符,value是Channel*通道的一个指针
  ChannelMap channels_;

 private:
  EventLoop* ownerLoop_;//Poller所属的EventLoop
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_POLLER_H

看看PollPoller类对poll的实现

PollPoller::poll()是PollPoller的核心功能,它调用poll()获得当前活动的IO事件, 然后填充调用方传入的activeChannels,并返回poll return的时刻

PollPoller::poll()

Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  /**
   * poll函数原型: 
   * int poll(struct pollfd *fds, unsigned long nfds, int timeout);
   * 返回值:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
   * 
   * 这里直接把vectorpollfds_传给poll
   * &*pollfds_.begin()是获得元素的首地址,表达式类型符合函数要求
   */
  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happended";

    // fillActiveChannels()会遍历pollfds_
    // 找出有活动事件的fd
    // 把它对应的Channel填入activeChannels
    fillActiveChannels(numEvents, activeChannels);//
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << " nothing happended";
  }
  else
  {
    LOG_SYSERR << "PollPoller::poll()";
  }
  return now;
}

PollPoller::fillActiveChannels()

遍历pollfds_, 找出有活动事件的fd, 把它对应的Channel填入activeChannels

void PollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const
{
  for (PollFdList::const_iterator pfd = pollfds_.begin();
      pfd != pollfds_.end() && numEvents > 0; ++pfd)
  {
    if (pfd->revents > 0)//产生了事件
    {
      // 每找到一个活动fd就递减numEvents
      --numEvents;
      // ChannelMap是从fd到Channel*的映射
      ChannelMap::const_iterator ch = channels_.find(pfd->fd);
      assert(ch != channels_.end());//断言通道是存在的
      Channel* channel = ch->second;
      assert(channel->fd() == pfd->fd);
      // 将当前活动事件revents保存到Channel中
      // 供Channel::handleEvent()使用
      channel->set_revents(pfd->revents);
      activeChannels->push_back(channel);
    }
  }
}

最后再看一下注册和更新IO事件的流程

用于注册或者更新通道
时序图:
muduo net库学习笔记2——muduo网络库相关类图的关系、EventLoop、Channel、 Poller_第3张图片
enableReading()函数会加入可读事件,然后执行Channel的Update()函数,Channel::Update()会调用EventLoop::updateChannel(),后者会转而调用Poller::updateChannel()。

PollPoller::updateChannel()

void PollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  // index()返回在poll的事件数组中的序号,index_在构造函数中的初始值为-1
  // index < 0说明是一个新的通道
  if (channel->index() < 0)
  {
    assert(channels_.find(channel->fd()) == channels_.end());
    struct pollfd pfd;
    pfd.fd = channel->fd();
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;
    pollfds_.push_back(pfd);
    // 加入到容器的最后一个位置,并设置它的序号
    int idx = static_cast<int>(pollfds_.size())-1;
    channel->set_index(idx);
    channels_[pfd.fd] = channel;
  }
  // 是已有的通道
  else
  {
    assert(channels_.find(channel->fd()) != channels_.end());
    assert(channels_[channel->fd()] == channel);
    int idx = channel->index();
    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
    struct pollfd& pfd = pollfds_[idx];
    assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;
    // 将通道暂时更改为不关注事件,但不从Poller中移除该通道
    if (channel->isNoneEvent())
    {
      // 如果某个Channel暂时不关心任何事件
      // 就把pfd.fd直接设置为-1,让poll忽略此项
      // 这里的设置是为了removeChannel优化
      pfd.fd = -channel->fd()-1; 
    }
  }
}

PollPoller::removeChannel()

void PollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd();
  assert(channels_.find(channel->fd()) != channels_.end());
  assert(channels_[channel->fd()] == channel);
  // removeChannel前需要先updateChannel为不关注事件
  assert(channel->isNoneEvent());
  int idx = channel->index();
  assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
  const struct pollfd& pfd = pollfds_[idx]; (void)pfd;
  assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());
  size_t n = channels_.erase(channel->fd());  // 移除该元素
  assert(n == 1); (void)n;
  // 如果恰好是最后一个元素,直接删除
  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)
  {
    pollfds_.pop_back();
  }
  else
  {
    // 将待删除元素与最后一个元素交换再pop_back,算法时间复杂度是O(1)
    int channelAtEnd = pollfds_.back().fd;
    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);
    if (channelAtEnd < 0)
    {
      channelAtEnd = -channelAtEnd-1;
    }
    channels_[channelAtEnd]->set_index(idx);
    pollfds_.pop_back();
  }
}

简要流程

Acceptor接收到客户端请求,调用TcpServer回调函数
TcpServer回调函数中创建TcpConnection对象,代表着一个Tcp连接
TcpConnection构造函数中创建Channel对象,保存客户端套接字fd和关心的事件(可读)
Channel 注册自己到所属事件驱动循环(EventLoop)中的Poller上
Poller开始监听,当发现Channel被激活后将其添加到EventLoop的激活队列中
EventLoop在poll返回后处理激活队列中的Channel,调用其处理函数
Channel在处理函数中根据被激活的原因调用不同的回调函数(可读/可写

先看一个具体的例子,最后再细节扣扣,不然是有点晕

你可能感兴趣的:(muduo源码)