muduo库的EpollPoller剖析

muduo---C++网络编程库 陈硕 muduo 参考资料 muduo源码剖析 muduo基于Reactor模式实现 Reactor模式也是目前大多数Linux端高性能网络编程框架和网络应用所选择的主要架构,例如内存数据库Redis和Java的Netty库等。 muduo 架构 整个架构依照reactor模式,如如下图: 所谓的reactor模型,是有一个循环的过程,监听对应事件是否触发,触发时调用对应的callback函数进行处理; 这里的事件在muduo中包括socket可读写事件、定时器事件。在其他网络库中如libevent也包 阅读详情


EpollPoller和PollPoller一样,都是muduo库对I/O复用机制的封装,不过默认使用的是EpollPoller。在EventLoop中初始化构造poller_,调用newDefaultPoller(this),构造默认的poller。

EventLoop::EventLoop()
  : looping_(false),  //表示还未循环
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),   //赋值真实id
    poller_(Poller::newDefaultPoller(this)),   //构造了一个实际的poller对象
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this, wakeupFd_)),
    currentActiveChannel_(NULL)

newDefaultPoller内部是这样的:

Poller* Poller::newDefaultPoller(EventLoop* loop)
{
  if (::getenv("MUDUO_USE_POLL"))   //看环境变量,如果没有MUDUO_UER_POLL,默认epoll
  {
    return new PollPoller(loop);
  }
  else
  {
    return new EPollPoller(loop);
  }
}

也就是说,如果我们没有主动设置环境变量为MUDUO_USE_POLL,我们默认选择为构造EpollPoller。

EpollPoller重写了父类的三个函数:

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

EpollPoller的私有成员有:

private:
  static const int kInitEventListSize = 16;   //默认事件数组大小,是用来装epoll_wait()返回的可读或可写事件的
  									

  static const char* operationToString(int op);

  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const;
  void update(int operation, Channel* channel);

  typedef std::vector<struct epoll_event> EventList;

  int epollfd_;
  EventList events_;  //用来存发生事件的数组

其中kInitEventListSize是epoll模式中用来盛装返回的发生事件的数组大小,就是events_,它在构造函数中会被初始化,看下面:

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),   //创建epollfd,使用带1的版本
    events_(kInitEventListSize)    //vector这样用时初始化kInitEventListSize个大小空间
{
  if (epollfd_ < 0)   //在构造函数中判断,<0就abort()
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}
有一点需要注意,就是vector的初始化方式,这样可以预分配大小。至于我们以后不断向epoll中注册事件,然后导致返回的发生事件可能越来越多,events_装不下了,muduo也有自己的处理,是在EpollPoller的核心函数poll()函数中处理的:

  if (implicit_cast<size_t>(numEvents) == events_.size())  //如果返回的事件数目等于当前事件数组大小,就分配2倍空间
    {																
      events_.resize(events_.size()*2);   
    }														       

所以不必担心vector的大小问题了,后续会以乘以2倍的方式分配,这也是内存分配的常见做法。

我们再来回忆一下事件处理流程,先看一下事件注册图:


当某个客端,比如TCPConnection,或者我们自己写的代码调用Channel的enableReading()函数,就会引发Channel的Update()函数的调用,然后会调用EventLoop的UpdateChannel()函数,实际上会调用到Poller的updateChannel()函数。比如采用EpollPoller,那就会将事件注册到epoll的内核事件表中去,然后开启epoll_wait(),就是我们正常的e'poll流程了。

然后看一下事件发生后处理的时序,先看图:


如图,EventLoop调用poll()是上面事件注册的过程。目前我们处于Poller位置,当Poller中事件发生后,会调用fillActiveChannels()函数,把发生的事件填充给活跃事件通道表中,具体实现是下面的Poller的成员函数fillActiveChannels函数:

void EPollPoller::fillActiveChannels(int numEvents,
                                     ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());//确定它的大小小于events_的大小,因为events_是预留的事件vector                                                                                    
  
  for (int i = 0; i < numEvents; ++i)   //挨个处理发生的numEvents个事件,epoll模式返回的events_数组中都是已经发生额事件,这有别于select和poll
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
/*
这是epoll模式epoll_event事件的数据结构,其中data不仅可以保存fd,也可以保存一个void*类型的指针。
typedef union epoll_data {
               void    *ptr;
               int      fd;
               uint32_t u32;
               uint64_t u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    // Epoll events 
               epoll_data_t data;      //User data variable 
           };
*/
    channel->set_revents(events_[i].events);   //把已发生的事件传给channel,写到通道当中
    activeChannels->push_back(channel);    //并且push_back进activeChannels
  }
}
最后一句push_back不言自明。我们再看EventLoop中核心函数也就是一直循环的loop()函数的处理:

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;  //true
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)  //遍历通道来进行处理
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;   //处理完了赋空
    eventHandling_ = false;  //false
    doPendingFunctors();   //
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}
当poll()返回时,loop()函数中会遍历活跃通道表activeChannels,并且执行它们每一个提前注册大的handleEvent()函数,处理完了会继续循环。


整个EpollPoller的作用就是这个样子了,接下来看完全的源码分析:

.h:

#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <vector>

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

#include <muduo/base/Mutex.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/TimerId.h>

namespace muduo
{
namespace net
{

class Channel;
class Poller;
class TimerQueue;

///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
class EventLoop : boost::noncopyable
{
 public:
  typedef boost::function<void()> Functor;

  EventLoop();
  ~EventLoop();  // force out-line dtor, for scoped_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();

  /// Quits loop.
  ///
  /// This is not 100% thread safe, if you call through a raw pointer,
  /// better to call through shared_ptr<EventLoop> for 100% safety.
  void quit();

  ///
  /// Time when poll returns, usually means data arrival.
  ///
  Timestamp pollReturnTime() const { return pollReturnTime_; }

  int64_t iteration() const { return iteration_; }

  /// Runs callback immediately in the loop thread.
  /// It wakes up the loop, and run the cb.
  /// If in the same loop thread, cb is run within the function.
  /// Safe to call from other threads.
  void runInLoop(const Functor& cb);
  /// Queues callback in the loop thread.
  /// Runs after finish pooling.
  /// Safe to call from other threads.
  void queueInLoop(const Functor& cb);

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  void runInLoop(Functor&& cb);
  void queueInLoop(Functor&& cb);
#endif

  // timers

  ///
  /// Runs callback at 'time'.
  /// Safe to call from other threads.
  ///
  TimerId runAt(const Timestamp& time, const TimerCallback& cb);
  ///
  /// Runs callback after @c delay seconds.
  /// Safe to call from other threads.
  ///
  TimerId runAfter(double delay, const TimerCallback& cb);
  ///
  /// Runs callback every @c interval seconds.
  /// Safe to call from other threads.
  ///
  TimerId runEvery(double interval, const TimerCallback& cb);
  ///
  /// Cancels the timer.
  /// Safe to call from other threads.
  ///
  void cancel(TimerId timerId);

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  TimerId runAt(const Timestamp& time, TimerCallback&& cb);
  TimerId runAfter(double delay, TimerCallback&& cb);
  TimerId runEvery(double interval, TimerCallback&& cb);
#endif

  // internal usage
  void wakeup();
  void updateChannel(Channel* channel);  //在poller中注册或者更新通道
  void removeChannel(Channel* channel);  //从poller中移除通道
  bool hasChannel(Channel* channel);

  // pid_t threadId() const { return threadId_; }
  void assertInLoopThread()
  {
    if (!isInLoopThread())  //如果不是,终止程序
    {
      abortNotInLoopThread();
    }
  }
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }  //比较线程id即可。
  // bool callingPendingFunctors() const { return callingPendingFunctors_; }
  bool eventHandling() const { return eventHandling_; }

  void setContext(const boost::any& context)
  { context_ = context; }

  const boost::any& getContext() const
  { return context_; }

  boost::any* getMutableContext()
  { return &context_; }

  static EventLoop* getEventLoopOfCurrentThread();

 private:
  void abortNotInLoopThread();
  void handleRead();  // waked up
  void doPendingFunctors();

  void printActiveChannels() const; // DEBUG

  typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */
  bool quit_; /* atomic and shared between threads, okay on x86, I guess. */
  bool eventHandling_; /* atomic */
  bool callingPendingFunctors_; /* atomic */
  int64_t iteration_;  
  const pid_t threadId_;   //当前所属对象线程id
  Timestamp pollReturnTime_;    //时间戳,poll返回的时间戳
  boost::scoped_ptr<Poller> poller_;  //poller对象
  boost::scoped_ptr<TimerQueue> timerQueue_;
  int wakeupFd_;
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  boost::scoped_ptr<Channel> wakeupChannel_;
  boost::any context_;

  // scratch variables
  ChannelList activeChannels_;   //Poller返回的活动通道,vector<channel*>类型
  Channel* currentActiveChannel_;   //当前正在处理的活动通道

  MutexLock mutex_;
  std::vector<Functor> pendingFunctors_; // @GuardedBy mutex_
};

}
}
#endif  // MUDUO_NET_EVENTLOOP_H
.cpp:

namespace
{
const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;
}

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),   //创建epollfd,使用带1的版本
    events_(kInitEventListSize)    //vector这样用时初始化kInitEventListSize个大小空间
{
  if (epollfd_ < 0)   //在构造函数中判断,<0就abort()
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}

EPollPoller::~EPollPoller()
{
  ::close(epollfd_);   //关闭
}

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()),    //使用epoll_wait(),等待事件返回,返回发生的事件数目
                               timeoutMs);  //epoll
  int savedErrno = errno;    //错误号
  Timestamp now(Timestamp::now());  //得到时间戳
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happended";
    fillActiveChannels(numEvents, activeChannels);  //调用fillActiveChannels,传入numEvents也就是发生的事件数目
    if (implicit_cast<size_t>(numEvents) == events_.size())  //如果返回的事件数目等于当前事件数组大小,就分配2倍空间
    {																//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      events_.resize(events_.size()*2);     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    }														       //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  }
  else if (numEvents == 0)  //返回=0
  {
    LOG_TRACE << "nothing happended";
  }
  else
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)   //如果不是EINTR信号,就把错误号保存下来,并且输入到日志中
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;  //返回时间戳
}

//把返回到的这么多个事件添加到activeChannels
/*protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;
*/
void EPollPoller::fillActiveChannels(int numEvents,
                                     ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());//确定它的大小小于events_的大小,因为events_是预留的事件vector
                                                                                       
/*typedef std::vector<struct epoll_event> EventList;   //modify by WY.Huang

  int epollfd_;
  EventList events_;
*/
  
  for (int i = 0; i < numEvents; ++i)   //挨个处理发生的numEvents个事件,epoll模式返回的events_数组中都是已经发生额事件,这有别于select和poll
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
/*
这是epoll模式epoll_event事件的数据结构,其中data不仅可以保存fd,也可以保存一个void*类型的指针。
typedef union epoll_data {
               void    *ptr;
               int      fd;
               uint32_t u32;
               uint64_t u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    // Epoll events 
               epoll_data_t data;      //User data variable 
           };
*/
#ifndef NDEBUG
    int fd = channel->fd();  //debug时做一下检测
    ChannelMap::const_iterator it = channels_.find(fd);
    assert(it != channels_.end());
    assert(it->second == channel);
#endif
    channel->set_revents(events_[i].events);   //把已发生的事件传给channel,写到通道当中
    activeChannels->push_back(channel);    //并且push_back进activeChannels
  }
}

//这个函数被调用是因为channel->enablereading()被调用,再调用channel->update(),再event_loop->updateChannel(),再->epoll或poll的updateChannel被调用
void EPollPoller::updateChannel(Channel* channel)   //更新通道
{
  Poller::assertInLoopThread();
  const int index = channel->index();   //channel是本函数参数,获得channel的index,初始状态index是-1
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted)  //index是在poll中是下标,在epoll中是三种状态,上面有三个常量
  {
    // a new one, add with EPOLL_CTL_ADD
    int fd = channel->fd();
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel;
    }
    else // index == kDeleted
    {
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }

    channel->set_index(kAdded);
    update(EPOLL_CTL_ADD, channel);
  }
  else
  {
    // update existing one with EPOLL_CTL_MOD/DEL
    int fd = channel->fd();
    (void)fd;
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(index == kAdded);  //确保目前它存在
   //
   //poll是把文件描述符变为相反数减1,这里是使用EPOLL_CTL_DEL从内核事件表中删除
   //
    if (channel->isNoneEvent())  //如果什么也没关注,就直接干掉
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted);     //删除之后设为deleted,表示已经删除,只是从内核事件表中删除,在channels_这个通道数组中并没有删除
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);   //有关注,那就只是更新。更新成什么样子channel中会决定。
    }
  }
}

void EPollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  int fd = channel->fd();
  LOG_TRACE << "fd = " << fd;
  assert(channels_.find(fd) != channels_.end());
  assert(channels_[fd] == channel);
  assert(channel->isNoneEvent());
  int index = channel->index();
  assert(index == kAdded || index == kDeleted);
  size_t n = channels_.erase(fd);
  (void)n;
  assert(n == 1);

  if (index == kAdded)
  {
    update(EPOLL_CTL_DEL, channel);
  }
  channel->set_index(kNew);
}

//epoll_ctl函数的封装
void EPollPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  bzero(&event, sizeof event);
  event.events = channel->events();
  event.data.ptr = channel;   //传入data.ptr
  int fd = channel->fd();
  LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
    << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)  //使用epoll_ctl
  {
    if (operation == EPOLL_CTL_DEL)  //如果delete失败,不退出
    {
      LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
    else
    {
      LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
  }
}

const char* EPollPoller::operationToString(int op)  //调试用的函数
{
  switch (op)
  {
    case EPOLL_CTL_ADD:
      return "ADD";
    case EPOLL_CTL_DEL:
      return "DEL";
    case EPOLL_CTL_MOD:
      return "MOD";
    default:
      assert(false && "ERROR op");
      return "Unknown Operation";
  }
}



[Muduo网络库]:三大核心库Channel类、Poller/EpollPoller类以及EventLoop类 除了循环,什么都没做的loop()",这句话的意思并非是loop()只是一个空循环,而是它的主要功能并未通过自身定义的函数实现。loop函数中主要通过调用一次Poller::poll方法它就能给你返回事件监听器的监听结果,然后调用这些Channel里面保管的不同类型事件的处理函数。loop()函数最主要的作用是实现循环,负责驱动“循环”的重要模块。Channel类其实相当于一个文件描述符的保姆!2.2.1 Channel类概述-我在地铁站里吃闸机。 阅读详情

相关推荐

剖析muduo网络库核心代码,重写muduo库

项目简介 模拟muduo库实现nonnon-blocking + IO-multiplexing + loop线程模型的高并发 TCP 服务器模型。 开发环境 Centos7 技术栈 C++、多线程、socket网络编程、epoll多路转接 项目设计 整体采用non-blocking + IO-multiplexing + loop线程的设计框架,其中线程模型采用one loop per thread的多线程服务端网络编程模型,结合reactor模型进行实现。 整体框架 Reactor模型 重要组件: E

weixin_43884775的博客 519

webserver服务器从零搭建到上线(八)|EpollPoller事件分发器类

EPollPoller 类实现了基于 epoll 的 I/O 多路复用,通过监控多个文件描述符上的事件,并在事件发生时通知相应的 Channel 对象来处理事件。通过实现这些函数,EPollPoller 能够高效地管理和分发事件。下一节,我们将讲解EventLoop类的具体实现!

糖炒栗子 1229

通俗讲解清楚光线追踪中的蒙特卡洛积分

彻底讲解光线追踪中的蒙特卡洛积分

qq_37366618的博客 1244

【Muduo】三大核心之Poller、EPollPoller

EPollPoller根据epoll_wait返回的文件描述符集合和事件类型,通过在epoll_event的data.ptr字段预先设置Channel对象的地址,从而能够找到对应的Channel对象,并调用其上的回调函数来处理事件。可以添加新的文件描述符、修改已注册的文件描述符的事件类型或删除已注册的文件描述符。:EPollPoller利用了epoll机制的高效性,能够同时监听大量的文件描述符,并且在有事件发生时只返回有事件发生的文件描述符集合,避免了轮询所有文件描述符的开销。

qq_20756957的博客 2394

C#实现跨域访问共享文件夹的账号密码验证方案

本文详细介绍了在C#中实现跨域访问共享文件夹的账号密码验证方案。通过调用Windows API LogonUser函数,结合LOGON32_LOGON_NEW_CREDENTIALS登录类型,可安全模拟指定用户身份,有效解决工作组与域环境间的文件共享访问难题。文章提供了完整的工具类封装、多种实战场景示例及详细的错误排查指南。

pea55的博客 542

光栅衍射有入射角时_衍射型多焦点人工晶体光学原理

多焦点人工晶体如何把光线分成多个焦点的?这个问题大部分专业书只是蜻蜓点水,并没有讲明白。希此文粗浅的解释,能帮您加深理解,请多指正。接下来,我将按照一下历史发展的顺序讲解一下:光的认识、菲涅尔灯塔(菲涅尔透镜)、菲涅尔波带盘(半波带盘)、多焦点人工晶体。一:首先复习一下光的认识历史。我们现在都知道光是一种电磁波,具有波粒二相性,但是认识光的本质也经历非常曲折漫长的过程。牛顿时代的几何光...

weixin_28963467的博客 2352

muduo-poller-epollPoller

EPollPoller : public poller epoll: epoll_create epoll_ctrl add/mod/del epoll_wait class EpollPoller : public Poller { public: EpollPoller(EventLoop *loop); ~EpollPoller() override; //重写基类的poll方法 TimeStamp poll(int timeout, ChannelList *activ

Tzecto 216

从模拟到实战:移远EC800M单片机MQTT接入OneNet全流程解析

本文详细解析了移远EC800M单片机通过MQTT协议接入OneNet平台的全流程,涵盖环境准备、模拟设备搭建、硬件迁移、数据交互调试及性能优化等关键步骤。针对常见问题如AT指令缓冲区溢出、心跳间隔设置等提供实战解决方案,帮助开发者高效实现物联网设备稳定接入。

weixin_29215815的博客 434

muduo库 EPollPoller

#pragma once #include "Poller.h" #include "Timestamp.h" #include <vector> #include <sys/epoll.h> class Channel; /** * epoll的使用 * epoll_create * epoll_ctl add/mod/del * epoll_wait */ class EPollPoller : public Poller { public:

weixin_50911232的博客 243

深入剖析:功率放大电路设计精髓——从理论到实践

输入信号首先被调制(通常是PWM脉宽调制)成高频开关信号,驱动功率开关管(MOSFET),再通过LC低通滤波器滤除高频分量,还原出放大的音频信号。在靠近功放板电源引脚处放置足够容量的电解电容(如100uF - 1000uF)和高频瓷片电容(如0.1uF)并联进行电源去耦,抑制低频和高频干扰,防止低频振荡(Motorboating)和高频自激。,堪称电子世界里的“能量转换器”。当输入信号在零附近(即两个器件切换导通的区域)时,由于器件的开启电压(Vbe或Vgs)不为零,输出波形会出现明显的衔接不流畅的失真。

Zihan2558的博客 1698

在jetson上对fastdeploy进行源码编译(完整版)

在大多数Linux发行版中,你可以使用包管理器安装Python开发包。根据jetpack的版本选择对应的推理库版本。在~/.bashrc中写入这三行。

如果想成为中心,那么就到中心去吧。 679

[muduo网络库]——muduo库三大核心组件之 Poller/EpollPoller类(剖析muduo网络库核心部分、设计思想)

muduo 是由陈硕大佬个人开发的 TCP 网络库,本专栏使用c++11重构了Muduo库中核心的Multi-Reactor架构,剖析大佬核心的代码设计思想,希望能够理清自己的思路,也希望能够帮助到大家。

m0_73537205的博客 1998

stata 工具变量法【计量经济系列(七)】

stata 工具变量法【计量经济系列(七)】 1. 内生性 2. 工具变量3. 2SLS (二阶段最小二乘法) 4. 弱工具变量5. 工具变量外生性的过度识别检验 6. 豪斯曼检验6.1 传统豪斯曼 6.2 杜宾-吴-豪斯曼检验(DWH)

侯小啾技术博客 2万+

Muduo库源码剖析(二)——Poller和EPollPoller

这个成员函数实现并不在 Poller.cc中,因为考虑到,这个函数功能是获取一个具体的Poller,如果在Poller.cc中实现就需要include EpollPoller或PollPoller, 这样在抽象基类里包含实现类头文件不好!执行其他程序的时候,全新的程序会替换子进程中的地址空间,数据段,堆栈,此时保存与父进程文件描述符也就不存在了,也无法进行关闭,这时候就需要。EventLoop中的ChannelList始终是 大于等于 Poller中的ChannelMap的。基类不应该依赖派生类。

qq_42120843的博客 649

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

重写muduo网络库:速览muduo组成结构

速览muduo组成结构

weixin_43973403的博客 197

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

muduo各个类梳理

文章目录一、Channel类二、Poller/EpollPoller类三、EventLoop类四、Thread、EventLoopThread、EventLoopThreadPool类五、Acceptor类六、Buffer类七、TcpConnection类八、TcpServer类 mainReactor就是我们代码中的mainloop(baseloop),subReactor是我们代码中的工作线程进行读写事件的处理,read是底层做的,decode、compute、encode是用户做的。从网络上read

长期更新自学笔记 433

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

长文梳理Muduo库核心代码及优秀编程细节剖析

长文梳理Muduo库核心代码(Multi-Reactor架构代码)及优秀编程细节思想剖析。

T_Solotov的博客 1万+

muduo网络库剖析——监听者EpollPoller类

基于c++的muduo网络库开源项目深度剖析!超级加分的简历项目,期待您的讨论~

weixin_62021811的博客 1287

[muduo网络库]——muduo库的Reactor模型(剖析muduo网络库核心部分、设计思想)

muduo 是由陈硕大佬个人开发的 TCP 网络库,本专栏使用c++11重构了Muduo库中核心的Multi-Reactor架构,剖析大佬核心的代码设计思想,希望能够理清自己的思路,也希望能够帮助到大家。

m0_73537205的博客 2032

国庆弯道超车(手写Muduo库剖析核心代码及编程思想)

Muduo库是陈硕老师个人编写的TCP网络编程库。博主出于学习的目的使用了C++11将陈硕老师的muduo库重构了一下,当然只是重构了其核心的思想。muduo库当中还有很多牛逼的实现个人现在能力有限只能看懂其主要的实现,后面能力提升了再写博客进行剖析。同时这篇博客的目的主要是为了剖析muduo库的核心代码梳理其代码的执行逻辑。这个了博主能力有限,可能写着写着就写出来了一些不知所云的东西,还请各位担待。如果有写错的地方请在下方留言指正。

qq_56999918的博客 2540

[muduo网络库]——muduo库三大核心组件之EventLoop类(剖析muduo网络库核心部分、设计思想)

muduo 是由陈硕大佬个人开发的 TCP 网络库,本专栏使用c++11重构了Muduo库中核心的Multi-Reactor架构,剖析大佬核心的代码设计思想,希望能够理清自己的思路,也希望能够帮助到大家。

m0_73537205的博客 2118

muduo源码剖析之poller/EpollPoller多路复用类

poller是I/O多路复用接口抽象虚基类,对I/O多路复用API的封装,muduo提供了EPollPoller和PollPoller派生类(epoll和poll),所以不支持select.newDefaultPoller()默认选择epoll。

新法的博客 1771
上一篇: muduo库的框架剖析及总结
下一篇: 7. Reverse Integer
FreeeLinux
博客等级 码龄11年 421粉丝 356原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值