muduo-net 源码分析(一)——EvenLoop

先序

对于事件的处理,一般来说都是使用 one loop pre thread + threadpool 的处理方式,也就是创建一个 I/O 线程用于监听文件描述符的事件,使用线程池来处理事件;每个线程只能创建一个 EvenLoop 对象,而创建 EvenLoop 的线程也就被称为 I/O 线程;

EvenLoop 的源码相当复杂,我们会先讲解 ChannelPoller 的源码,对于一些后面用到的代码我们会先省略不说;

这一节的主要思路在于理清 EvenLoop、Poller 和 Channel 的关系

Channel 源码分析

重要的成员变量

对于文件套接字以及其对应的监听事件和触发事件都保存在 Channel 中;又因为 Channel 需要在其对应的 EvenLoop 中被管理,所以还会保存一个 EvenLoop 对象;
index 是一个很重要的参数,会在 Poller 中使用到,这里先不说;

EventLoop* loop_;     			//记录当前 channel 所属的 EvenLoop
const int  fd_;       			//套接字
int        events_;   			//关注事件
int        revents_;  			//触发事件
int        index_;    			//
bool       logHup_;

bool eventHandling_;                //是否处于处理事件中
bool addedToLoop_;                  //是否添加到 EvenLoop 中

//三个事件类型
static const int kNoneEvent;      
static const int kReadEvent;
static const int kWriteEvent;

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;   
const int Channel::kWriteEvent = POLLOUT;

//四种事件的函数指针
ReadEventCallback readCallback_;    
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;

/*以下变量本节并不讲解
std::weak_ptr<void> tie_;		
bool tied_;
*/

重要的成员函数

//构造函数和析构函数,只传递文件描述符和所属的 EvenLoop 对象
Channel(EventLoop* loop, int fd);
~Channel();

//事件处理函数
void handleEvent(Timestamp receiveTime);
void handleEventWithGuard(Timestamp receiveTime);

//注册、修改和删除事件
//注册和修改最终都是调用 update 函数
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(); }
void remove();
void update();

//设置 Channel 的四种事情发生时调用的函数
typedef std::function<void()> EventCallback;      
typedef std::function<void(Timestamp)> ReadEventCallback;  
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); }

/*以下函数本节并不讲解
void tie(const std::shared_ptr<void>&);
*/

具体函数实现

//构造函数,注意 index_ 默认是 -1,events_ 默认是 kNoneEvent 即没有任何事件
Channel::Channel(EventLoop* loop, int fd__)
  : loop_(loop),
    fd_(fd__),
    events_(0),
    revents_(0),
    index_(-1),
    logHup_(true),
    tied_(false),
    eventHandling_(false),
    addedToLoop_(false)
{
}

//析构函数只能在所属的 loop 中调用
Channel::~Channel()
{
  assert(!eventHandling_);
  assert(!addedToLoop_);
  if (loop_->isInLoopThread())
  {
    assert(!loop_->hasChannel(this));
  }
}

//调用 EvenLoop 的 updateChannel 函数
void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);
}

//调用 EvenLoop 的 removeChannel 函数
void Channel::remove()
{
  //注意在调用 remove 之前一定要先调用 disableAll(),不然断言会失败
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);
}

//handleEvent 实际上就是调用 handleEventWithGuard
void Channel::handleEvent(Timestamp receiveTime)
{
  /* 这一部分本节不讲解
  std::shared_ptr<void> guard;
  if (tied_)
  {
    //weak_ptr 的提升
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  */
  else
  {
    handleEventWithGuard(receiveTime);
  }
}


//handleEventWithGuard() 函数本质上就是根据 revents 的类型来调用发生事件的函数
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;
}

具体的函数实现后面会说,大致的过程如下:

EvenLoop ep
Channel ch(&ep, fd);
ch.setReadCallback(std::move(func));
ch.enableReading();
ch.disableAll();
ch.remove();

Poller 源码分析

muduo 中的 Poller 实际上是一个虚基类,实际的实现在 muduo/poller 目录下;也就是说 muduo 库中主要使用了 poll/epoll 进行事件监听;

重要的成员变量

EventLoop* ownerLoop_;			//所属的 EvenLoop 对象
typedef std::map<int, Channel*> ChannelMap;   //int 是文件描述符 
//注意 channels_ 访问级别是 protected,基类是可以访问的
ChannelMap channels_;			//使用 map 的原因是可以通过 key 快速定位 value

重要的成员变量及实现

//构造函数传递 EvenLoop 对象
Poller(EventLoop* loop)
  : ownerLoop_(loop)
{
}

virtual ~Poller() = default;

typedef std::vector<Channel*> ChannelList;

//非常重要的函数
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;

virtual void updateChannel(Channel* channel) = 0;

virtual void removeChannel(Channel* channel) = 0;

//该函数只能在所属的 evenloop 中调用
//功能就是查找当前 IO 线程是否否在 channel 对象,这也是用 map 的原因
bool hasChannel(Channel* channel) const
{
  assertInLoopThread();
  ChannelMap::const_iterator it = channels_.find(channel->fd());
  return it != channels_.end() && it->second == channel;
}

static Poller* newDefaultPoller(EventLoop* loop);

//根据环境变量生成一个子类对象传递给父类指针
//默认是使用 EPOLL 模式
Poller* Poller::newDefaultPoller(EventLoop* loop)
{
  if (::getenv("MUDUO_USE_POLL"))
  {
    return new PollPoller(loop);
  }
  else
  {
    return new EPollPoller(loop);
  }
}

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

具体的实现先不说,主要是要理清每个类的主要作用

EvenLoop 源码分析

EvenLoop 中的变量和函数很多,我们分开说;

重要的成员变量

bool looping_; 						//是不是在调用 poll() 中
std::atomic<bool> quit_;      		//是否退出
bool eventHandling_;				//是否在处理事件

//bool callingPendingFunctors_;		本节不讨论		

int64_t iteration_;

const pid_t threadId_;				//创建 EvenLoop 对象的线程 tid            
Timestamp pollReturnTime_;       	//调用 poll() 的时间戳,因为 readbackCall 需要一个时间戳
std::unique_ptr<Poller> poller_;  	//基类指针,这里使用 unique_ptr,说明 poller 的生命周期受 EvenLoop 管理

typedef std::vector<Channel*> ChannelList;
ChannelList activeChannels_;   		//EvenLoop 中活跃的通道
Channel* currentActiveChannel_;		//现在处理的通道

/*本节不讨论
std::unique_ptr<TimerQueue> timerQueue_;  

int wakeupFd_;                    

std::unique_ptr<Channel> wakeupChannel_;    
boost::any context_;   

mutable MutexLock mutex_;
std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
*/
  • 本节中最主要的变量就是 threadId_poller_activeChannels_
  • 一个线程只能有一个 EvenLoop,一个 EvenLoop 只能有一个 Poller,但一个 EvenLoop 可以有多个 Channel

主要的成员函数

//本节只讨论一下函数,有一些返回参数的函数自己看看就行了
EventLoop();
~EventLoop();
void loop();
void quit();
void updateChannel(Channel* channel);       
void removeChannel(Channel* channel);       
bool hasChannel(Channel* channel);

//assertInLoopThread 函数是一个经常使用的函数,因为大部分的 EvenLoop 操作都需要在当前线程中使用
void assertInLoopThread()
{
  if (!isInLoopThread())
  {
    abortNotInLoopThread();
  }
}

//判断当前线程是不是 IO 线程
bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }

具体函数实现

__thread EventLoop* t_loopInThisThread = 0;   //线程的局部变量

//EvenLoop 需要判断当前线程有没有创建过 EvenLoop 对象
//创建一个 poller 对象
EventLoop::EventLoop()
  : looping_(false),
    quit_(false),
    eventHandling_(false),
    //callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),		//获取创建 EvenLoop 对象的线程 tid
    poller_(Poller::newDefaultPoller(this)),	//这里是返回一个派生类指针,面向对象的思路
    //timerQueue_(new TimerQueue(this)),
    //wakeupFd_(createEventfd()),
    //wakeupChannel_(new Channel(this, wakeupFd_)),
    currentActiveChannel_(NULL)
{
  LOG_DEBUG << "EventLoop created " << this << " in thread " << threadId_;
  //判断当前线程是否已经存在 EvenLoop,如果已经存在则报错
  //一开始 t_loopInThisThread 为 0
  if (t_loopInThisThread)
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this;
  }
  
  /*
  wakeupChannel_->setReadCallback(
      std::bind(&EventLoop::handleRead, this));
  wakeupChannel_->enableReading();
  */
}

//主要是将 t_loopInThisThread  置为 NULL
EventLoop::~EventLoop()
{
  LOG_DEBUG << "EventLoop " << this << " of thread " << threadId_
            << " destructs in thread " << CurrentThread::tid();
  /*
  wakeupChannel_->disableAll();
  wakeupChannel_->remove();
  ::close(wakeupFd_);
  */
  t_loopInThisThread = NULL;
}


//重点函数之一,只能在 IO 线程中调用
void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();
  looping_ = true;
  quit_ = false;  
  LOG_TRACE << "EventLoop " << this << " start looping";

  //循环条件,不退出
  while (!quit_)
  {
    //清空活跃列表
    activeChannels_.clear();
    
    //实际上 EvenLoop 的 poll() 函数就是调用 Poller 派生类的 poll() 函数
 	//在这里我们认为 poll() 函数是返回调用 poll() 的时间戳,以及返回 activeChannels_
	pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    
    ++iteration_;

	//输出日志
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }

    eventHandling_ = true;

    //遍历所有活跃事件,调用活跃事件的 handleEvent 函数
    //前面说过 Channel 的 handleEvent() 是根据活跃通道的 revent 来调用对应函数
    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;
  /*
  if (!isInLoopThread())
  {
    wakeup();
  }
  */
}

//只能在 IO 线程中调用,实际上调用的是 Poller 的 updateChannel
//Channel 的 update 调用的是 EvenLoop 的 updateChannel,但实际都是调用 Poller 的 updateChannel
void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}

//只能在 IO 线程中调用,实际上调用的是 Poller 的 removeChannel
//Channel 的 update 调用的是 EvenLoop 的 removeChannel,但实际都是调用 Poller 的 removeChannel
void EventLoop::removeChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  if (eventHandling_)
  {
    assert(currentActiveChannel_ == channel ||
        std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
  }
  poller_->removeChannel(channel);
}

bool EventLoop::hasChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  return poller_->hasChannel(channel);
}

void EventLoop::abortNotInLoopThread()
{
  LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
            << " was created in threadId_ = " << threadId_
            << ", current thread id = " <<  CurrentThread::tid();
}

总结

根据目前分析的函数和变量总结一下 ChannelPollerEvenLoop 的关系;

  1. 首先在当前线程创建一个 EvenLoop 对象,这里会默认创建一个 Poller 对象;【默认是 epoll
  2. 创建多个 Channel 对象,传递 EvenLoop 对象以及他们的 fd
  3. 设置 Channel 的事件函数,并设置 Channel 对象的监听事件,enableReading()、enableWriting()…,这些函数会自动调用 update() 函数,最终调用 PollerupdateChannel() 函数;【uodateChannel() 的作用?
  4. 开启循环,调用 EvenLooploop(),实际调用的是 Poller 中的 poll() 函数,然后处理活跃事件的任务;
  5. 关闭 Channel 的监听事件,调用 remove() ,实际上调用的是 PollerremoveChannel() 函数;
  6. 退出循环,调用 EvenLoopquit()

下一节需要处理的 Poller 的派生类,主要是 poll()、updateChannel() 和 removeChannel();我们主要以 poll 作为讲解,epollpoll 差不多,对于不同的地方会做出讲解;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值