mymuduo笔记

209 阅读8分钟

类有哪些基本功能,实现了哪些实例,从构造函数看出来(看成员变量不是很直观,有些成员只有类的成员函数在使用);构造函数暴露的接口(入参)越少,类阅读起来思路越清晰,功能越内聚,特别是传入的其他类型的指针越少,与其他对象的耦合就越小

类保有其他类型的指针,最好不要像操作二级指针一样去操作这个指针,这就要求:设计这个指向的类时,它不要把自己的成员变量暴露出去

EPollPoller只有一种形态,就是Poller::newDefaultPoller(this)它只被EventLoop所持有,也就是说EPollPoller依附在一个EventLoop身上,即EventLoopEPollPoller一一对应

EventLoop只在两个地方存在:

    1. 主线程中
    1. EventLoopThread::threadFunc函数栈上,并且之后调用了EventLoop::loop()进入了"死"循环
    1. EventLoop依赖线程,依附在一个线程中执行,所以有个成员变量是const pid_t threadId
  • 一, EventLoop构造函数与创建eventFd的静态函数
    // 创建wakeupfd,用来notify唤醒subReactor处理新来的channel
    int createEventfd()
    {
        int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
        if (evtfd < 0)
        {
            LOG_FATAL("eventfd error:%d \n", errno);
        }
        return evtfd;
    }  
    
    // EventLoop 构造函数
    EventLoop::EventLoop()
        : looping_(false)
        , quit_(false)
        , callingPendingFunctors_(false)
        , threadId_(CurrentThread::tid())
        , poller_(Poller::newDefaultPoller(this))
        , wakeupFd_(createEventfd())
        , wakeupChannel_(new Channel(this, wakeupFd_))
    {
        LOG_DEBUG("EventLoop created %p in thread %d \n", this, threadId_);
        if (t_loopInThisThread)
        {
            LOG_FATAL("Another EventLoop %p exists in this thread %d \n", t_loopInThisThread, threadId_);
        }
        else
        {
            t_loopInThisThread = this;
        }
    
        // 设置wakeupfd的事件类型以及发生事件后的回调操作
        wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
        // 每一个eventloop都将监听wakeupchannel的EPOLLIN读事件了
        wakeupChannel_->enableReading();
    }
    
  • 二, 可以看出EventLoop:
    • 持有一个EPollPoller命名为poller_
      EventLoopEPollPoller一一对应关系

    • 持有一个::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)的返回值wakeupFd_(也只有EventLoop才持有eventFd)
      只有EventLoop会持有它,是一一对应

    • 持有一个用wakeupFd_构造的Channel,把它命名成wakeupChannel_
      EventLoop与用wakeupFd_构造的Channel也是一一对应的

    • 构造函数里的wakeupChannel_->enableReading();最终是执行下面(最终是::epoll_ctl(epollfd_, operation, fd, &event)):
      EventLoop用其所保有的EPollPoller执行下面代码,入参是在刚才构造出的wakeupChannel_(new Channel(this, wakeupFd_))

      void EPollPoller::updateChannel(Channel *channel)
      {
          const int index = channel->index();
          LOG_INFO("func=%s => fd=%d events=%d index=%d \n", __FUNCTION__, channel->fd(), channel->events(), index);
      
          if (index == kNew || index == kDeleted)
          {
              if (index == kNew)
              {
                  int fd = channel->fd();
                  channels_[fd] = channel;
              }
      
              channel->set_index(kAdded);
              update(EPOLL_CTL_ADD, channel);
          }
          else  // channel已经在poller上注册过了
          {
              int fd = channel->fd();
              if (channel->isNoneEvent())
              {
                  update(EPOLL_CTL_DEL, channel);
                  channel->set_index(kDeleted);
              }
              else
              {
                  update(EPOLL_CTL_MOD, channel);
              }
          }
      }
      
    • 从上面代码亦可看出EPollPollerchannels_首次挂载的是eventFd

      using ChannelMap = std::unordered_map<int, Channel*>;
      ChannelMap channels_;
      
  • 三, 什么时候会创建EventLoop?
      1. 主线程中
      1. 下面函数中
    // 下面这个方法,实在单独的新线程里面运行的
    void EventLoopThread::threadFunc()
    {
        EventLoop loop; // 创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread
    
        if (callback_)
        {
            callback_(&loop);
        }
    
        {
            std::unique_lock<std::mutex> lock(mutex_);
            loop_ = &loop;
            cond_.notify_one();
        }
    
        loop.loop(); // EventLoop loop  => Poller.poll
        std::unique_lock<std::mutex> lock(mutex_);
        loop_ = nullptr;
    }
    

EPollPoller在构造的时候首先创建并保有一份epoll实例,以下是其构造函数

EPollPoller::EPollPoller(EventLoop *loop)
    : Poller(loop)
    , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
    , events_(kInitEventListSize)  // vector<epoll_event>
{
    if (epollfd_ < 0)
    {
        LOG_FATAL("epoll_create error:%d \n", errno);
    }
}
  • EPollPoller是项目上唯一一个对epoll_create1epoll_waitepoll_ctl封装的类

Channel只有三种形态

  • 1, new Channel(this, wakeupfd) --> 区别eventFd
    该对象被EventLoop所持有
  • 2, new Channel(loop, sockfd) --> 区别通信socket
    该对象被TcpConnection所持有
  • 3, Channel acceptChannel_; --> 区别监听socket
    该对象被Acceptor所持有
    (AcceptorTcpServer一样,只有唯一一个实例,封装了监听socket),以成员变量的形式持有Acceptor
    Acceptor构造函数以参数初始化列表构造(用主线程中的EventLoop*和监听socket构造而成)
  • 4, Channel构造函数,从中能看出,构造时没做实质性工作,就是在接纳外部传进来的指针(EventLoop *, 文件描述符)
    Channel::Channel(EventLoop *loop, int fd)
        : loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false)
    {
    }
    

第一阶段分析到此为止,即执行到InetAddress addr(8000, "0.0.0.0");此时只有:

  • 一个EventLoop
  • 一个Channel(用eventFd构造的,并将eventFd挂载在了epoll树上)
  • 一个EPollPoller依附在主线程(主函数)中的EventLoop身上
    int main()
    {
        // 第一阶段
        EventLoop loop;
        InetAddress addr(8000, "0.0.0.0");  
        // 第二阶段  
        EchoServer server(&loop, addr, "EchoServer-01"); // Acceptor non-blocking listenfd  create bind 
        // 第三阶段
        server.start(); // listen  loopthread  listenfd => acceptChannel => mainLoop =>
        loop.loop(); // 启动mainLoop的底层Poller
        return 0;
    }
    
  • 第一阶段,主要看主线程中的EventLoop的工作,最终是将创建的第一个eventFd通过epoll_ctl挂载到epoll树上

第二阶段分析从TcpServer开始

  • EchoServer构造函数
    EchoServer(EventLoop *loop,
                const InetAddress &addr, 
                const std::string &name)
            : server_(loop, addr, name)
            , loop_(loop)
        {
            // 注册回调函数
            server_.setConnectionCallback(
                std::bind(&EchoServer::onConnection, this, std::placeholders::_1)
            );
    
            server_.setMessageCallback(
                std::bind(&EchoServer::onMessage, this,
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
            );
    
            // 设置合适的loop线程数量 loopthread
            server_.setThreadNum(2);
        }
        void start()
        {
            server_.start();
        }
    
    • TcpServer对象保有主线程中的EventLoop*
  • TcpServer构造函数
    static EventLoop* CheckLoopNotNull(EventLoop *loop)
    {
        if (loop == nullptr)
        {
            LOG_FATAL("%s:%s:%d mainLoop is null! \n", __FILE__, __FUNCTION__, __LINE__);
        }
        return loop;
    }  
    // TcpServer 构造函数  
    TcpServer::TcpServer(EventLoop *loop,
                    const InetAddress &listenAddr,
                    const std::string &nameArg,
                    Option option)
                    : loop_(CheckLoopNotNull(loop))
                    , ipPort_(listenAddr.toIpPort())
                    , name_(nameArg)
                    , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
                    , threadPool_(new EventLoopThreadPool(loop, name_))
                    , connectionCallback_()
                    , messageCallback_()
                    , nextConnId_(1)
                    , started_(0)
    {
        // 当有先用户连接时,会执行TcpServer::newConnection回调
        acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, 
            std::placeholders::_1, std::placeholders::_2));
    }
    
    • 持有一份Acceptor*(用主线程中的EventLoop*构造)
      注意:AcceptorTcpServer也是一一对应的(只有TcpServer才持有Acceptor)
      整个程序中独一份的Acceptor
    • 持有一份EventLoopThreadPool*,同样也是根据主线程中的EventLoop*构造出的
      EventLoopThreadPoolTcpServer一一对应
      整个程序中独一份的EventLoopThreadPool
  • Acceptor构造函数与创建监听socket的静态函数
    // 创建监听socket
    static int createNonblocking()
    {
        int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
        if (sockfd < 0) 
        {
            LOG_FATAL("%s:%s:%d listen socket create err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);
        }
        return sockfd;
    }    
    
    // 构造函数
    Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
        : loop_(loop)
        , acceptSocket_(createNonblocking()) // socket
        , acceptChannel_(loop, acceptSocket_.fd())
        , listenning_(false)
    {
        acceptSocket_.setReuseAddr(true);
        acceptSocket_.setReusePort(true);
        acceptSocket_.bindAddress(listenAddr); // bind
        // TcpServer::start() Acceptor.listen  有新用户的连接,要执行一个回调(connfd=》channel=》subloop)
        // baseLoop => acceptChannel_(listenfd) => 
        acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
    }
    
    • 持有一份监听的socketacceptSocket_(createNonblocking())
    • 根据监听的socket(监听文件描述符)和主线程中的EventLoop*生成一份Channel并持有它,即acceptChannel_(loop, acceptSocket_.fd())
  • EventLoopThreadPool构造函数
    EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg)
        : baseLoop_(baseLoop)
        , name_(nameArg)
        , started_(false)
        , numThreads_(0)
        , next_(0)
    {}
    
    • 构造函数里没做具体的事,保有主线程中的EventLoop*

TcpConnection构造函数

TcpConnection::TcpConnection(EventLoop *loop, 
                const std::string &nameArg, 
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr)
    : loop_(CheckLoopNotNull(loop))
    , name_(nameArg)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
    , highWaterMark_(64*1024*1024) // 64M
{
    // 下面给channel设置相应的回调函数,poller给channel通知感兴趣的事件发生了,channel会回调相应的操作函数
    channel_->setReadCallback(
        std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
    );
    channel_->setWriteCallback(
        std::bind(&TcpConnection::handleWrite, this)
    );
    channel_->setCloseCallback(
        std::bind(&TcpConnection::handleClose, this)
    );
    channel_->setErrorCallback(
        std::bind(&TcpConnection::handleError, this)
    );

    LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
    socket_->setKeepAlive(true);
}

第二阶段到此为止,主要是构造EchoServer server(&loop, addr, "EchoServer-01");对象,也即TcpServer对象

  • 该对象(TcpServer)构造完成后,即创建了唯一的监听socket,但并没有立即将其挂载到epoll树上,TcpServerstart()方法才将监听socket挂载到epoll树上
  • 构造过程:
    • TcpServerAcceptor,TcpServerEventLoopThreadPool都是一一对应关系
    • EventLoop* loop_;都是指主线程中的那个EventLoop
classDiagram
TcpServer o-- Acceptor
TcpServer o-- EventLoopThreadPool
Acceptor *-- Socket
Acceptor *-- Channel
class TcpServer {
    - EventLoop* loop_;
    // 运行在mainLoop,任务就是监听新连接事件  
    // Acceptor 与 TcpServer 一一对应 
    - std::unique_ptr<Acceptor> acceptor_;
    // EventLoopThreadPool 与 TcpServer 一一对应       
    - std::shared_ptr<EventLoopThreadPool> threadPool_; 
}
class Acceptor {
    - EventLoop* loop_; 
}
class EventLoopThreadPool {
    - EventLoop* loop_;     
    // 管理根据线程数量创建的 EventLoopThread 对象
    - std::vector<std::unique_ptr<EventLoopThread>> threads_;  
    // 管理容纳在 EventLoopThread::threadFunc 中的 EventLoop 对象   
    - std::vector<EventLoop*> loops_;
}

第三阶段分析main函数中的server.start();loop.loop();函数

  • 有个前提条件server_.setThreadNum(2);设置sub_loop的数量为2
  • server.start();总览:
    执行过程(将1执行两遍,因为设置的子loop数为2):
      1. EventLoop(关联eventFd) --> EventLoop::loop()在子线程中执行loop * 2
      1. 将监听socket挂载到epoll树上
    • TcpServer::start()函数
      // 开启服务器监听   loop.loop()
      void TcpServer::start()
      {
          if (started_++ == 0) // 防止一个TcpServer对象被start多次
          {
              threadPool_->start(threadInitCallback_); // 启动底层的loop线程池
              loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
          }
      }
      
      • void EventLoopThreadPool::start(const ThreadInitCallback &cb)
        下面函数中创建EventLoopThread并用std::vector<std::unique_ptr<EventLoopThread>> threads_;盛放
        void EventLoopThreadPool::start(const ThreadInitCallback &cb)
        {  
            // 说明入参,实际回调 cb 未被实例化,可以看成是 nullptr
            started_ = true;
        
            for (int i = 0; i < numThreads_; ++i)
            {
                char buf[name_.size() + 32];
                snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
                EventLoopThread *t = new EventLoopThread(cb, buf);
                threads_.push_back(std::unique_ptr<EventLoopThread>(t));
                loops_.push_back(t->startLoop()); // 底层创建线程,绑定一个新的EventLoop,并返回该loop的地址
            }
        
            // 整个服务端只有一个线程,运行着baseloop
            if (numThreads_ == 0 && cb)
            {
                cb(baseLoop_);
            }
        }
        
        • EventLoopThread::EventLoopThread构造函数
          EventLoopThread对象在构造函数中先构造Thread对象,名为thread_(为和上面相区分,摘录它作为成员变量的定义Thread thread_;)
          注意:实际的多线程函数运行时的实例是EventLoopThread::threadFunc后面会给出函数定义
          // 说明:经过观察,入参的回调对象未被实现,可以理解成 cb = nullptr
          EventLoopThread::EventLoopThread(const ThreadInitCallback &cb, 
          const std::string &name)
          : loop_(nullptr)
          , exiting_(false)
          , thread_(std::bind(&EventLoopThread::threadFunc, this), name)
          , mutex_()
          , cond_()
          , callback_(cb)
          {
          }
          
          实际的多线程运行的代码实体,注意它里面创建了EventLoop loop对象
          至此,这个局部对象又执行loop.loop()函数,即进入"死"循环
          非常合理!在子线程中执行"死"循环,(要等将子线程start(),即上面代码执行t->startLoop())
          // 下面这个方法,是在单独的新线程里面运行的
          void EventLoopThread::threadFunc()
          {
              EventLoop loop; // 创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread
          
              if (callback_)
              {
                  callback_(&loop);
              }
          
              {
                  std::unique_lock<std::mutex> lock(mutex_);
                  loop_ = &loop;
                  cond_.notify_one();
              }
          
              loop.loop(); // EventLoop loop  => Poller.poll
              std::unique_lock<std::mutex> lock(mutex_);
              loop_ = nullptr;
          }
          
          Thread封装了std::thread
          class Thread : noncopyable
          {
          public:
              using ThreadFunc = std::function<void()>;
          
              explicit Thread(ThreadFunc, const std::string &name = std::string());
          
        • t->startLoop()定义,注意看里面有thread_.start();在里面启动多线程
          EventLoop* EventLoopThread::startLoop()
          {
              thread_.start(); // 启动底层的新线程
          
              EventLoop *loop = nullptr;
              {
                  std::unique_lock<std::mutex> lock(mutex_);
                  while ( loop_ == nullptr )
                  {
                      cond_.wait(lock);
                  }
                  loop = loop_;
              }
              return loop;
          }
          
          thread_.start();定义 :
          void Thread::start()  // 一个Thread对象,记录的就是一个新线程的详细信息
          {
              started_ = true;
              sem_t sem;
              sem_init(&sem, false, 0);
          
              // 开启线程
              thread_ = std::shared_ptr<std::thread>(new std::thread([&](){
                  // 获取线程的tid值
                  tid_ = CurrentThread::tid();
                  sem_post(&sem);
                  // 开启一个新线程,专门执行该线程函数
                  func_(); 
              }));
          
              // 这里必须等待获取上面新创建的线程的tid值
              sem_wait(&sem);
          }
          
      • void Acceptor::listen()
        void Acceptor::listen()
        {
            listenning_ = true;
            acceptSocket_.listen(); // listen
            acceptChannel_.enableReading(); // acceptChannel_ => Poller
        }
        


再看

EventLoop是个通信类,它持有的EpollPollerChannel(eventFd构造的)通过EventLoop通信;特别是Channel通过EventLoop*拿到EpollPoller*,并将自己Channel*传递给它

Q:有个EventLoop对象代表了什么?

  • A:一个EventLoop对象表示:
    持有EpollPoller对象
    持有eventFd(用作异步通知的fd)
    持有Channel(this, wakeupFd_)
    以及继续完善Channel(在EventLoop构造函数中做的工作)
    • 完善Channel::ReadEventCallback readCallback_;变量
      std::bind(&EventLoop::handleRead, this, std::placeholders::_1)把回调函数(readCallback_对象)设置成这个样子
    • wakeupFd_挂载到由EventLoop所管理的EpollPollerepoll树上

简单看,只有EventLoop loop;loop.loop();会发生什么?

  • A:上面已经分析了EventLoop loop;对象,下面贴出EventLoop::loop代码
    EventLoop::loop
    // 开启事件循环
    void EventLoop::loop()
    {
        looping_ = true;
        quit_ = false;
    
        LOG_INFO("EventLoop %p start looping \n", this);
    
        while(!quit_)
        {
            activeChannels_.clear();
            // 监听两类fd   一种是client的fd,一种wakeupfd
            pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
            for (Channel *channel : activeChannels_)
            {
                // Poller监听哪些channel发生事件了,然后上报给EventLoop,通知channel处理相应的事件
                channel->handleEvent(pollReturnTime_);
            }
            // 执行当前EventLoop事件循环需要处理的回调操作
            /**
             * IO线程 mainLoop accept fd《=channel subloop
             * mainLoop 事先注册一个回调cb(需要subloop来执行)    wakeup subloop后,执行下面的方法,执行之前mainloop注册的cb操作
             */ 
            doPendingFunctors();
        }
    
        LOG_INFO("EventLoop %p stop looping. \n", this);
        looping_ = false;
    }
    

__thread EventLoop *t_loopInThisThread = nullptr;很趣,作用是:防止一个线程创建多个EventLoop thread_local

// 构造函数中这么用,用它作判断  
if (t_loopInThisThread)
{
    LOG_FATAL("Another EventLoop %p exists in this thread %d \n", t_loopInThisThread, threadId_);
}
else
{
    t_loopInThisThread = this;
}  
// 析构时  
t_loopInThisThread = nullptr;