muduo库的PollPoller类剖析


muduo库中唯一使用面向对象的地方就在Poller,它有两个派生类,分别是PollPoller和EPollPoller。可以实现两种I/O多路复用机制。

Poller

Poller的数据成员有:
protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;

 private:
  EventLoop* ownerLoop_;
Poller使用一个map来存放描述符fd和对应的Channel类型的指针,这样我们就可以通过fd很方便的得到Channel了。私有成员是一个EventLoop的指针,用来指向当前EventLoop,用来判断防止Poller被跨线程调用。

下面是Poller类分析:
#ifndef MUDUO_NET_POLLER_H
#define MUDUO_NET_POLLER_H

#include <map>
#include <vector>
#include <boost/noncopyable.hpp>

#include <muduo/base/Timestamp.h>
#include <muduo/net/EventLoop.h>

namespace muduo
{
namespace net
{

class Channel;

///
/// Base class for IO Multiplexing
///
/// This class doesn't own the Channel objects.
class Poller : boost::noncopyable
{
 public:
  typedef std::vector<Channel*> ChannelList;  //取别名ChannelList表示channel*类型数组

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

  /// Polls the I/O events.
  /// Must be called in the loop thread.
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;

  /// Changes the interested I/O events.
  /// Must be called in the loop thread.
  virtual void updateChannel(Channel* channel) = 0;

  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  virtual void removeChannel(Channel* channel) = 0;

  virtual bool hasChannel(Channel* channel) const;

  static Poller* newDefaultPoller(EventLoop* loop);

  void assertInLoopThread() const
  {
    ownerLoop_->assertInLoopThread();
  }

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

 private:
  EventLoop* ownerLoop_;
};

}
}
#endif  // MUDUO_NET_POLLER_H
.cc文件:
#include <muduo/net/Poller.h>

#include <muduo/net/Channel.h>

using namespace muduo;
using namespace muduo::net;

Poller::Poller(EventLoop* loop)
  : ownerLoop_(loop)
{
}

Poller::~Poller()
{
}

bool Poller::hasChannel(Channel* channel) const  //返回是否有channel
{
  assertInLoopThread();
  ChannelMap::const_iterator it = channels_.find(channel->fd());
  return it != channels_.end() && it->second == channel;
}


PollPoller

PollPoller是对Poller的一个实现,底层采用poll模式。
PollPoller的数据成员有:
private:
  typedef std::vector<struct pollfd> PollFdList;
  PollFdList pollfds_;
这是一个存放pollfd的数组,用来传入poll模式中的第一个事件集合参数:
 int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);

它的分析如下:
#ifndef MUDUO_NET_POLLER_POLLPOLLER_H
#define MUDUO_NET_POLLER_POLLPOLLER_H

#include <muduo/net/Poller.h>

#include <vector>

struct pollfd;

namespace muduo
{
namespace net
{

///
/// IO Multiplexing with poll(2).
///
class PollPoller : public Poller
{
 public:

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

  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);  //返回通道列表
  virtual void updateChannel(Channel* channel);
  virtual void removeChannel(Channel* channel);

 private:
  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const;

  typedef std::vector<struct pollfd> PollFdList;
  PollFdList pollfds_;
};

}
}
#endif  // MUDUO_NET_POLLER_POLLPOLLER_H
.cc文件如下:
#include <muduo/net/poller/PollPoller.h>

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

#include <assert.h>
#include <errno.h>
#include <poll.h>

using namespace muduo;
using namespace muduo::net;

PollPoller::PollPoller(EventLoop* loop)
  : Poller(loop)
{
}

PollPoller::~PollPoller()
{
}

Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  // XXX pollfds_ shouldn't change    //传入数组首地址,大小,超时
  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
  int savedErrno = errno;
  Timestamp now(Timestamp::now());  //时间戳
  if (numEvents > 0)  //说明有事件发生
  {
    LOG_TRACE << numEvents << " events happended";
    fillActiveChannels(numEvents, activeChannels);  //放入活跃事件通道中
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << " nothing happended";
  }
  else
  {
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "PollPoller::poll()";
    }
  }
  return now;
}

//向活跃事件通道数组放入活跃事件
void PollPoller::fillActiveChannels(int numEvents,
                                    ChannelList* activeChannels) const
{
  for (PollFdList::const_iterator pfd = pollfds_.begin();
      pfd != pollfds_.end() && numEvents > 0; ++pfd)  
  {
    if (pfd->revents > 0)  //>=说明产生了事件
    {
      --numEvents;  //处理一个减减
      //map<int, channel*>  文件描述符和通道指针的map
      ChannelMap::const_iterator ch = channels_.find(pfd->fd);//调用map.find函数
      assert(ch != channels_.end()); //断言找到事件
      Channel* channel = ch->second;   //获取事件
      assert(channel->fd() == pfd->fd);  //断言相等
      channel->set_revents(pfd->revents);   //设置要返回的事件类型
      // pfd->revents = 0;
      activeChannels->push_back(channel);  //加入活跃事件数组
    }
  }
}

//用于注册或者更新通道
void PollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();  //断言只能在I/O线程中调用
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  if (channel->index() < 0)  //如果小于0,说明还没有注册?
  {
    // a new one, add to pollfds_
    assert(channels_.find(channel->fd()) == channels_.end());//断言查找不到
    struct pollfd pfd;
    pfd.fd = channel->fd();
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;  
    pollfds_.push_back(pfd);  //合成pollfd
    int idx = static_cast<int>(pollfds_.size())-1;  //idx放在末尾,因为是最新的,这个idx是pollfd_的下标
    channel->set_index(idx);  //idx是channel的成员变量
    channels_[pfd.fd] = channel; //加入map中
  }
  else   //如果大于0,说明是更新一个已存在的通道
  {
    // update existing one
    assert(channels_.find(channel->fd()) != channels_.end());
    assert(channels_[channel->fd()] == channel);
    int idx = channel->index();   //取出channel的下标
    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
    struct pollfd& pfd = pollfds_[idx];  //获取pollfd,以引用方式提高效率
    assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);  //-channel->fd()-1是因为匹配下面暂时不关注状态的设置
    pfd.events = static_cast<short>(channel->events());   //更新events
    pfd.revents = 0;
    //将一个通道暂时更改为不关注事件,但不从Poller中移除通道,可能下一次还会用
    if (channel->isNoneEvent())  
    {
      // ignore this pollfd
      //暂时忽略该文件描述符的时间
      //这里pfd.fd可以直接设置为-1
      pfd.fd = -channel->fd()-1;   //这样子设置是为了removeChannel优化
    }
  }
}

//这个是真正移除pollfd的程序
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());  //一定已经没有事件上的关注了,所以在调用removeChannel之前必须
  int idx = channel->index();  //取出在pollfds_数组下标中的位置
  assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
  const struct pollfd& pfd = pollfds_[idx]; (void)pfd;  //得到pollfd
  assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());
  size_t n = channels_.erase(channel->fd()); //使用键值移除从map中移除
  assert(n == 1); (void)n;
  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)  //如果是最后一个
  {
    pollfds_.pop_back();  //直接pop_back()
  }
  else  //不是vector最后一个,这个问题就来了!vector移除前面元素可能会涉及到吧后面元素挪动,所以不可取。
  {       //这里采用的是交换的方法,类似于图算法中删除顶点,我们将要移除的元素和最后一个元素交换即可。
  			//这里算法时间复杂度是O(1)
    int channelAtEnd = pollfds_.back().fd;   //得到最后一个元素
    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);  //交换
    if (channelAtEnd < 0)  //如果最后一个<0
    {
      channelAtEnd = -channelAtEnd-1;  //把它还原出来,得到真实的fd,这就是不使用-1的原因,我们不关注它了
      					//把它改为-fd-1,然后删除的时候可以再还原回来。如果改为-1就不可以了。
    }
    channels_[channelAtEnd]->set_index(idx); //对该真实的fd更新下标
    pollfds_.pop_back();  //弹出末尾元素
  }
}

注意它对不关注事件的fd的修改技巧,这是一个值得学习的地方。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值