【Muduo源码分析1】总体框架+事件循环机制

文章中不免有错误之处,如若发现,恳请指正,感恩不尽!

mudou库事件循环机制总体实现逻辑理解

主从Reactor之间交互

  • 主Reactor主要负责接收新连接请求,然后将该连接发送给从Reactor进行处理
  • 从Reactor则是监听主Reactor交付给它的请求,然后对其进行处理

单个Reactor执行逻辑

  • EventLoop不断调用poll来监听多个文件描述符的事件
  • 当poll检测到某个文件描述符上事件发生的时候,会通知EventLoop
  • EventLoop根据事件找到对应的Channel,然后通过Channel预先注册好的回调函数,来处理对应的事件

新连接与Channel的关系

总体架构

代码分析范围

各模块的主要功能

  • EventLoop 管理事件循环,调度各类事件的处理。
  • Poller 监听并管理所有的I/O事件。
  • Channel 将具体的文件描述符与事件处理函数关联起来。

各模块逻辑分析

EventLoop模块

事件循环的核心模块,主要负责事件的启动和维护事件循环的运行,处理不同的事件,该模块管理着poller和Channel模块。
核心成员函数和变量

cpp 复制代码
class EventLoop : noncopyable
{
 public:
  typedef std::function<void()> Functor;

  EventLoop();
  ~EventLoop();

  void loop();
  void quit();
  
  void runInLoop(Functor cb);
  void queueInLoop(Functor cb);

  void wakeup();
  void updateChannel(Channel* channel);
  void removeChannel(Channel* channel);
  bool hasChannel(Channel* channel);

 private:
  void abortNotInLoopThread();
  void handleRead();
  void doPendingFunctors();

  typedef std::vector<Channel*> ChannelList;
  
  bool looping_;
  std::atomic<bool> quit_;
  bool eventHandling_;
  bool callingPendingFunctors_;
  int64_t iteration_;
  const pid_t threadId_;
  Timestamp pollReturnTime_;
  std::unique_ptr<Poller> poller_;
  std::unique_ptr<TimerQueue> timerQueue_;
  int wakeupFd_;
  std::unique_ptr<Channel> wakeupChannel_;
  ChannelList activeChannels_;
  Channel* currentActiveChannel_;

  mutable MutexLock mutex_;
  std::vector<Functor> pendingFunctors_;
};

核心模块:loop方法,通过调用poller和poll方法来获取当前已经就绪的新连接文件描述符,然后调用事先已经设置好的回调函数,执行其逻辑

cpp 复制代码
void EventLoop::loop()
{
    assert(!looping_);
    looping_ = true;
    quit_ = false;

    while (!quit_) {
        activeChannels_.clear();
        pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
        for (Channel* channel : activeChannels_) {
            channel->handleEvent(pollReturnTime_);
        }
        doPendingFunctors();
    }

    looping_ = false;
}

poller模块

对多路复用机制的封装

核心成员函数和变量

cpp 复制代码
class Poller : noncopyable
{
 public:
  typedef std::vector<Channel*> ChannelList;

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

  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
  virtual void updateChannel(Channel* channel) = 0;
  virtual void removeChannel(Channel* channel) = 0;

  virtual bool hasChannel(Channel* channel) const;

 protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;

 private:
  EventLoop* ownerLoop_;
};

核心方法说明:不停的遍历是否有文件描述符就绪,如果有就绪的文件描述符,则将其加入到就绪队列中,然后交付给EventLoop去处理这些已经就绪的文件描述符

cpp 复制代码
Timestamp Poller::poll(int timeoutMs, ChannelList* activeChannels)
{
    int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
    Timestamp now(Timestamp::now());
    if (numEvents > 0) {
        for (PollFdList::const_iterator pfd = pollfds_.begin(); pfd != pollfds_.end() && numEvents > 0; ++pfd) {
            if (pfd->revents > 0) {
                --numEvents;
                ChannelMap::const_iterator ch = channels_.find(pfd->fd);
                Channel* channel = ch->second;
                channel->set_revents(pfd->revents);
                activeChannels->push_back(channel);
            }
        }
    }
    return now;
}

Channel模块

主要负责管理文件描述符与具体回调函数绑定,在事件发生的时候,调用对应的回调函数

cpp 复制代码
class Channel : noncopyable
{
 public:
  typedef std::function<void()> EventCallback;
  typedef std::function<void(Timestamp)> ReadEventCallback;

  Channel(EventLoop* loop, int fd);
  ~Channel();

  void handleEvent(Timestamp receiveTime);
  void setReadCallback(ReadEventCallback cb);
  void setWriteCallback(EventCallback cb);
  void setCloseCallback(EventCallback cb);
  void setErrorCallback(EventCallback cb);

  void tie(const std::shared_ptr<void>&);
  
  void enableReading();
  void enableWriting();
  void disableWriting();
  void disableAll();
  
  bool isNoneEvent() const;
  bool isWriting() const;
  bool isReading() const;

  int fd() const;
  int events() const;
  void set_revents(int revt);

 private:
  void update();
  void handleEventWithGuard(Timestamp receiveTime);

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

  EventLoop* loop_;
  const int  fd_;
  int        events_;
  int        revents_;
  int        index_;
  bool       logHup_;
  bool       tied_;
  bool       eventHandling_;
  bool       addedToLoop_;
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

核心方法,根据实际发生的事件类型,调用对应的回调函数,然后处理事件

cpp 复制代码
void Channel::handleEvent(Timestamp receiveTime)
{
    eventHandling_ = true;
    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;
}

测试

验证Channel的事件处理流程

观察EventLoop的创建与Channel之间的关系

测试源码

  • 编译命令:g++ -g -I/home/muduo -I/home/muduo/net Channel_test.cpp -o Channel_test -L/home/muduo/lib -lmuduo_net -lmuduo_base
cpp 复制代码
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>

using namespace muduo;

void handleRead(muduo::Timestamp receiveTime) {
    std::cout << "Data received at " << receiveTime.toFormattedString() << std::endl;
}

int main() {
    int listenFd = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    bind(listenFd, (sockaddr*)&serverAddr, sizeof(serverAddr));
    listen(listenFd, 5);

    muduo::net::EventLoop loop;
    muduo::net::Channel channel(&loop, listenFd);
    channel.setReadCallback(handleRead);
    channel.enableReading(); // 假设有这个方法来使能读事件

    loop.loop();

    return 0;
相关推荐
Charles Ray29 分钟前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码29 分钟前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
爱上语文31 分钟前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
编程零零七3 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
2401_858286114 小时前
52.【C语言】 字符函数和字符串函数(strcat函数)
c语言·开发语言
铁松溜达py4 小时前
编译器/工具链环境:GCC vs LLVM/Clang,MSVCRT vs UCRT
开发语言·网络
everyStudy4 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
C-SDN花园GGbond5 小时前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
迷迭所归处6 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ7 小时前
Java 23 的12 个新特性!!
java·开发语言·学习