muduo库分析——net篇(2)EpollPoller和PollPoller

本文对比了Epoll和Poll两种I/O多路复用技术。Epoll通过高效地管理大量文件描述符,提供了更高的性能。Poll则适用于较简单的场景。两者的主要区别在于Epoll使用更高效的事件驱动模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    这两个类主要是对IO模型Epoll和Poll的一个封装,功能是调用Epoll和Poll相关系统函数,注册删除更新IO文件描述符,返回活动的通道。

    Poller.h

 protected:
  typedef std::map<int, Channel*> ChannelMap;	
  ChannelMap channels_;		// fd,Channel 集合

 private:
  EventLoop* ownerLoop_;

基类实现了hasChannel函数,判断Map是否拥有此Channel

基类持有Loop和Map


PollPoller

  typedef std::vector<struct pollfd> PollFdList;	//文件描述符集合
  PollFdList pollfds_;
Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels) //Poll具体实现
{
  // XXX pollfds_ shouldn't change
  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);	//poll系统调用,阻塞在这里等待IO数据到来
  int savedErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happened";
    fillActiveChannels(numEvents, activeChannels);	//根据事件个数填写活动通道
  }
.....
}
void PollPoller::fillActiveChannels(int numEvents,
                                    ChannelList* activeChannels) const
{
	//遍历整个文件描述符数组,找到标志位(revents)被更改的描述符
  for (PollFdList::const_iterator pfd = pollfds_.begin();
      pfd != pollfds_.end() && numEvents > 0; ++pfd)
  {
    if (pfd->revents > 0)
    {
      --numEvents;	//活动个数减1控制循环
      ChannelMap::const_iterator ch = channels_.find(pfd->fd);	//根据文件描述符fd索引到channel
      assert(ch != channels_.end());	//断言是否是channel集合末尾(ch为空)
      Channel* channel = ch->second;	//获得其value值
      assert(channel->fd() == pfd->fd);	//如果fd<0则表示该channel暂停工作
      channel->set_revents(pfd->revents);	//channel设置事件状态
      // pfd->revents = 0;
      activeChannels->push_back(channel);
    }
  }
}
//更新channel
void PollPoller::updateChannel(Channel* channel)	
{
  Poller::assertInLoopThread();	//断言是Poller是在本线程工作
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  if (channel->index() < 0)	//index创建时默认-1表示新的channel
  {
    // a new one, add to pollfds_
    assert(channels_.find(channel->fd()) == channels_.end());	//断言该fd不是已有channel
    struct pollfd pfd;	//添加poll文件描述符
    pfd.fd = channel->fd();	
    pfd.events = static_cast<short>(channel->events());	//添加感兴趣的事件
    pfd.revents = 0;
    pollfds_.push_back(pfd);
    int idx = static_cast<int>(pollfds_.size())-1;	//将文件描述符下标返回
    channel->set_index(idx);
    channels_[pfd.fd] = channel;
  }
  else
  {
    // update existing one
    assert(channels_.find(channel->fd()) != channels_.end());	//断言此fd是存在的
    assert(channels_[channel->fd()] == channel);
    int idx = channel->index();
    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
    struct pollfd& pfd = pollfds_[idx];
    assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);
    pfd.fd = channel->fd();
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;
    if (channel->isNoneEvent())	//设置channel为忽略事件时fd取反-1
    {
      // ignore this pollfd
      pfd.fd = -channel->fd()-1;	//作用是 fd为0时,取反还是0,因此-1
    }
  }
}
void PollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd();
  assert(channels_.find(channel->fd()) != channels_.end());
  assert(channels_[channel->fd()] == channel);
  assert(channel->isNoneEvent());	//注意要先设置为忽略事件
  int idx = channel->index();
  assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
  const struct pollfd& pfd = pollfds_[idx]; (void)pfd;
  assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());
  size_t n = channels_.erase(channel->fd());
  assert(n == 1); (void)n;
  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)	//如果删除的通道不在最后一个元素,则调换数组两个元素
  {
    pollfds_.pop_back();
  }
  else
  {
    int channelAtEnd = pollfds_.back().fd;
    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);
    if (channelAtEnd < 0)	//最后一个元素小于0则需要改变为正值再找到对应channel设置下标
    {
      channelAtEnd = -channelAtEnd-1;
    }
    channels_[channelAtEnd]->set_index(idx);
    pollfds_.pop_back();
  }
}


EpollPoller

  typedef std::vector<struct epoll_event> EventList;

  int epollfd_;
  EventList events_;
void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  const int index = channel->index();	//获得当前IO Channel状态
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted)	//kNew是表示当前IO Channel是新的,并且需要添加进epoll IO文件描述符中
											//kDeleted表示IO Channel之前存在,现在需要添加进epoll IO文件描述符中
  {
    // 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);		//统一设置为已经添加进epoll的状态
    update(EPOLL_CTL_ADD, channel);	//对update设置add的操作
  }
  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);
    if (channel->isNoneEvent())	//忽略channel 所在epoll中的文件描述符
    {
      update(EPOLL_CTL_DEL, channel);
      channel->set_index(kDeleted);	//当前channel状态设置为已删除
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);	//更新channel的感兴趣事件
    }
  }
}
Epoll和Poll差别就在于update上面,主要是Epoll的index和operation一一对应,而Poll的index是数组下标





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值