muduo_net源码分析(1):Reactor的关键结构

Muduo网络库简介

muduo是一个基于Reactor模式的现代c++网络库。它主要采用非阻塞_IO模型,基于时间驱动和回调,原生支持多核多线程,适合编写Linux服务端多线程网络应用程序

Reactor的关键结构

Reacor的核心是事件分发机制,也就是将IO复用模型(poll/epoll)那里拿到的io事件分发给各文件描述符的事件处理函数。为了实现这个机制,我们需要编写Channel class和Poller class。下面说明一下这两个类。
类图

1 Channel class

Channel是为文件描述符fd服务的,它并不占有fd,亦不会在析构的时候关闭它。Channel只会属于一个EventLoop,也只属于一个IO线程。最后需要说明的是用户一般不会直接使用Channel,而回使用更上一层的封装。下面看一下Channel的代码

class Channel : boost::noncopyable
{
		 public:
		  typedef boost::function<void()> EventCallback;
		  typedef boost::function<void(Timestamp)> ReadEventCallback;
		
		  Channel(EventLoop* loop, int fd);
		  ~Channel();
		
		  void handleEvent(Timestamp receiveTime);
		  void setReadCallback(const ReadEventCallback& cb)
		  { readCallback_ = cb; }
		  void setWriteCallback(const EventCallback& cb)
		  { writeCallback_ = cb; }
		  void setCloseCallback(const EventCallback& cb)
		  { closeCallback_ = cb; }
		  void setErrorCallback(const EventCallback& cb)
		  { errorCallback_ = cb; }
		
		  /// Tie this channel to the owner object managed by shared_ptr,
		  /// prevent the owner object being destroyed in handleEvent.
		  void tie(const boost::shared_ptr<void>&);
		
		  int fd() const { return fd_; }
		  int events() const { return events_; }
		  void set_revents(int revt) { revents_ = revt; } // used by pollers
		  // int revents() const { return revents_; }
		  bool isNoneEvent() const { return events_ == kNoneEvent; }
		
		  void enableReading() { events_ |= kReadEvent; update(); }
		  // void disableReading() { events_ &= ~kReadEvent; update(); }
		  void enableWriting() { events_ |= kWriteEvent; update(); }
		  void disableWriting() { events_ &= ~kWriteEvent; update(); }
		  void disableAll() { events_ = kNoneEvent; update(); }
		  bool isWriting() const { return events_ & kWriteEvent; }
		
		  // for Poller
		  int index() { return index_; }
		  void set_index(int idx) { index_ = idx; }
		
		  // for debug
		  string reventsToString() const;
		
		  void doNotLogHup() { logHup_ = false; }
		
		  EventLoop* ownerLoop() { return loop_; }
		  void remove();
		
		 private:
		  void update();
		  void handleEventWithGuard(Timestamp receiveTime);
		
		  static const int kNoneEvent;
		  static const int kReadEvent;
		  static const int kWriteEvent;
		
		  EventLoop* loop_;//所属Eventloop
		  const int  fd_;//文件描述符,但不负责关闭该文件描述符
		  int        events_;//关注的事件
		  int        revents_;//poll/epoll返回的事件
		  int        index_; // used by Poller.表示poll的事件数组中的序号
		  bool       logHup_;//for pollhup
		
		  boost::weak_ptr<void> tie_;
		  bool tied_;
		  bool eventHandling_;//是否处于处理事件中
		  ReadEventCallback readCallback_;
		  EventCallback writeCallback_;
		  EventCallback closeCallback_;
		  EventCallback errorCallback_;
};

1.1 数据成员

Channel的关键数据成员中,events_是它关心的IO事件,有用户设置。revents_是目前的活动事件,由EventLoop/Poller设置;而index_是channel所服务的fd所对应的struct pollfd数组的下标。而update函数会调用EventLoop的update函数,继而调用Poller的update函数。

1.2 handleEvent函数

这个函数由EventLoop::loop调用,它的功能是依据revents_的值分别调用不同的用户回调。

2 Poller类

Poller是一个抽象基类,这个类是IO复用模型的封装,后面会实现PollPoller(支持Poll)或者EpollPoller(支持Epoll)来继承它。

class Poller : boost::noncopyable
{
	 public:
	  typedef std::vector<Channel*> ChannelList;
	
	  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;
	
	  static Poller* newDefaultPoller(EventLoop* loop);
	
	  void assertInLoopThread()
	  {
	    ownerLoop_->assertInLoopThread();
	  }
	
	 private:
	  EventLoop* ownerLoop_;
};

Poller的虚函数poll完成Poller的核心功能:获取当前活动的IO事件,然后将其加入activeChannels。updateChannel和removeChannel分别用于将一个Channel对应的fd从struct pollfd数组中加入或去除。

2.1 PollPoller 类

PollPoller类继承了Poller,使用poll函数实现IO复用。当前我们就使用PollPoller,后续会补充EpollPoller。PollPoller类的fillActiveChannels是找出有事件的fd,填入activeChannels。ChannelMap是文件描述符和Channel指针的一个map。

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;
	  typedef std::map<int, Channel*> ChannelMap;
	  PollFdList pollfds_;
	  ChannelMap channels_;
};

2.1.1 updateChannel函数

这个函数的作用维护和更新pollfd_数组。它可以为数组增加文件描述符,也可以暂时忽略某文件描述符的事件或者重新关注它。注意在忽略文件描述符时,采用的方式时将它的文件描述符取反减一,可以帮助remove。(减1是为了文件描述符可能为0)。

void PollPoller::updateChannel(Channel* channel)
{
	  Poller::assertInLoopThread();
	  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
	  if (channel->index() < 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);
	    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());
	    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.events = static_cast<short>(channel->events());
	    pfd.revents = 0;
	    if (channel->isNoneEvent())
	    {
	      // ignore this pollfd
	      pfd.fd = -channel->fd()-1;
	    }
	  }
}

2.1.2 removeChannel函数

removeChannel函数复杂度为O(1),它采用的方法是交换需要删除的Channel和数组中最后一位Channel,然后pop_back。

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)
    {
      channelAtEnd = -channelAtEnd-1;
    }
    channels_[channelAtEnd]->set_index(idx);
    pollfds_.pop_back();
  }
}

小结

在我们完成上述两个类之后,Reactor的核心内容构造完毕。在EventLoop的loop函数中,我们利用Poller可以得到活跃的Channel,然后便可以分别处理这些活跃的Channel,如此循环。时序图如下:

时序图
注册和更新IO事件流程 时序图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值