首页 > 其他分享 >【muduo】net篇---Poller

【muduo】net篇---Poller

时间:2023-08-29 12:32:55浏览次数:31  
标签:assert muduo epoll --- _. fd channels net channel


Poller类负责更新某一个Channel(或者说套接字)上对应的事件,通过epoll_wait()函数返回有事件发生的套接字,设置Channel上的事件(revents_),并添加到激活事件列表中。

#include <muduo/net/poller/EPollPoller.h>
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>

#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <sys/epoll.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
static_assert(EPOLLIN == POLLIN,        "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI,      "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT,      "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP,  "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR,      "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP,      "epoll uses same flag values as poll");

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

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)), // 创建epoll文件描述符
    events_(kInitEventListSize)
{
  if (epollfd_ < 0)
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}

EPollPoller::~EPollPoller()
{
  ::close(epollfd_); // 关闭epoll文件描述符
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-15 
Description : 调用epoll_wait()获得当前活动的I/O事件,并调用
fillActiveChannels()找出有活动事件的fd,将其填充到activeChannels。
*********************************************************************/
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_TRACE << "fd total count " << channels_.size();
  // 调用epoll的等待函数,等待事件的发生,epoll_wait函数返回之后,events_中存放着已经发生的事件
  int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin(),
                               static_cast<int>(events_.size()),
                               timeoutMs);
  // 保存错误码
  int savedErrno = errno;
  // 当前时间
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happened";
    // 遍历事件列表,填充已激活事件处理器的列表
    fillActiveChannels(numEvents, activeChannels);
    // 调整事件列表的大小
    if (implicit_cast<size_t>(numEvents) == events_.size())
    {
      events_.resize(events_.size()*2);
    }
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << "nothing happened";
  }
  else
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-15
Description : 遍历事件列表,找出有活动事件的fd,把它对应的channel
填入activeChannels。
*********************************************************************/
void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());
  // 把已经激活事件的事件处理器添加到已激活事件处理器列表中
  for (int i = 0; i < numEvents; ++i)
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUG
    int fd = channel->fd();
    ChannelMap::const_iterator it = channels_.find(fd);
    assert(it != channels_.end());
    assert(it->second == channel);
#endif
    // events_[i].events中存放了发生的事件,在通道中设置事件
    channel->set_revents(events_[i].events);
    activeChannels->push_back(channel);
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-15
Description : 更新事件处理器,操作的方式取决于Channel的index字段,
默认是-1,表示新。
*********************************************************************/
void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  // 返回某一个channel对应在channels_的下标
  const int index = channel->index();
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted)
  {
    int fd = channel->fd();
    // 如果是新增事件,把事件处理器存放到channels_(是一个map)中
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel;
    }
    else
    {
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }
    // 更新文件描述符(epoll专用的文件描述符)
    channel->set_index(kAdded);
    update(EPOLL_CTL_ADD, channel);
  }
  // 如果是删除或修改
  else
  {
    int fd = channel->fd();
    (void)fd;
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(index == kAdded);
    // 删除文件描述符
    if (channel->isNoneEvent())
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted);
    }
    // 修改文件描述符
    else
    {
      update(EPOLL_CTL_MOD, channel);
    }
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-15
Description : 删除事件处理器。
*********************************************************************/
void EPollPoller::removeChannel(Channel* channel)
{
  // 判断是否在IO线
  Poller::assertInLoopThread();
  int fd = channel->fd();
  LOG_TRACE << "fd = " << fd;
  assert(channels_.find(fd) != channels_.end());
  assert(channels_[fd] == channel);
  assert(channel->isNoneEvent());
  // 获得pfd位置的索引
  int index = channel->index();
  assert(index == kAdded || index == kDeleted);
  // 在map中删除channel
  size_t n = channels_.erase(fd);
  (void)n;
  assert(n == 1);
  if (index == kAdded)
  {
    update(EPOLL_CTL_DEL, channel);
  }
  channel->set_index(kNew);
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-15
Description : 调用epoll_ctl()注册要监听的事件类型。
*********************************************************************/
void EPollPoller::update(int operation, Channel* channel)
{
  // 构建一个epoll事件
  struct epoll_event event;
  memZero(&event, sizeof event);
  // 存放事件处理器中需要处理的事件和事件处理器
  event.events = channel->events();
  event.data.ptr = channel;
  // 事件处理器对应的描述符
  int fd = channel->fd();
  LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
    << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
  // epoll的事件注册函数
  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
  {
    if (operation == EPOLL_CTL_DEL) // 从epfd中删除一个fd
    {
      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";
  }
}


标签:assert,muduo,epoll,---,_.,fd,channels,net,channel
From: https://blog.51cto.com/u_6526235/7274967

相关文章

  • 【muduo】常见的并发网络服务程序设计方案
    文章目录一、IO复用1、select模型2、poll模型3、epoll模型二、单线程Reactor三、Reactor+ThreadPool四、MultipleReactors(oneloopperthread)一、IO复用目前常用的IO复用模型有三种:select,poll,epoll。1、select模型说的通俗一点就是各个客户端连接的文件描述符也就是套接字,都......
  • 【muduo】TCP分包和Buffer类的设计
    文章目录一、TCP分包问题1、长连接和短连接2、长连接和短连接的分包方法3、长连接和短连接的应用场景二、TCP粘包问题三、Buffer类的设计与使用1、为什么需要应用层buffer?2、如何设计并使用应用层Buffer?3、Buffer类的设计一、TCP分包问题在TCP这种字节流协议上做应用层分包是网络......
  • 论文阅读 《Pingmesh: A Large-Scale System for Data Center Network Latency Measur
    背景在我们内部产品中,一直有关于网络性能数据监控需求,我们之前是直接使用ping命令收集结果,每台服务器去ping(N-1)台,也就是N^2的复杂度,稳定性和性能都存在一些问题,最近打算对这部分进行重写,在重新调研期间看到了Pingmesh这篇论文,Pingmesh是微软用来监控数据中心网络情况......
  • 2023年DAMA-CDGA/CDGP数据治理认证线上到这里学习
    DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业竞争能力。DAMA是数据管理方面的认证,帮助数据从业者提升......
  • 2023年9月DAMA-CDGA/CDGP数据治理认证考试,火热报名
    据DAMA中国官方网站消息,2023年度第三期DAMA中国CDGA和CDGP认证考试定于2023年9月23日举行。 报名通道现已开启,相关事宜通知如下: 考试科目: 数据治理工程师(CertifiedDataGovernanceAssociate,CDGA)数据治理专家(CertifiedDataGovernanceProfessional,CDGP) 考试时间: CDGA:2023......
  • iTOP-i.MX8MM开发板添加RIL驱动程序库
    将Quectel提供的相应RIL库文件放入Android系统的以下路径。作者拷贝到了源码的android_build/device/fsl/imx8m/evk_8mm/lib目录下,如下图所示:然后将apns-conf.xml拷贝到android_build/device/fsl/imx8m/evk_8mm/下,如下图所示:......
  • 智能菜谱系统-计算机毕业设计源码+LW文档
    1.1研究背景自古以来,烹饪食品一直是人类的基本需求之一,烹饪技术的不断发展和创新,为人们带来了不同的美食体验。科技进步的同时又在不断地加快人们的生活节奏,越来越忙碌的生活节奏使得人们能够花费在制作美食上的时间越来越少;同时,随着生活水平的提高,人们对健康饮食的需求也日益增长......
  • 幼儿园管理系统-计算机毕业设计源码+LW文档
    摘 要现在人们对学前教育越来越重视,幼儿教育发展迅速,幼儿的数量也在大大增加,导致幼儿园的管理工作变得愈加繁重。以报表的方式管理幼儿园信息资料,不仅不方便园中资料的存储和查看,还加重了园中的管理工作、减低了工作效率。现在,大多数幼儿园都缺少一个向外界展现自身特色的平台,幼......
  • Flutter系列文章-Flutter在实际业务中的应用
    不同场景下的解决方案1.跨平台开发:在移动应用开发中,面对不同的平台(iOS和Android),我们通常需要编写两套不同的代码。而Flutter通过一套代码可以构建适用于多个平台的应用,大大提高了开发效率,降低了维护成本。2.混合开发:在一些已有的原生应用中,引入Flutter可以用于开发某些特定......
  • [代码随想录]Day30-贪心算法part04
    题目:860.柠檬水找零思路:收到钱三种情况:5刀:直接收起来就可以了,不需要找钱10刀:收到10刀,需要找5刀,如果没有5刀,就返回false,否则5刀-120刀:收到20刀(但是没用,找钱也不能找20所以不需要记录数量),优先考虑找105,因为10只能在这里用,其次再考虑找555代码:funclemonadeChange(bil......