muduo源码学习(15)-IO复用的封装

来源:互联网 发布:美团大数据 在哪看 编辑:程序博客网 时间:2024/05/18 00:37

上篇从大体上介绍了muduo网络库部分的类和封装的思路,现在具体分析其中的类。首先从简单的IO复用的封装开始。

在分析IO复用之前首先要分析Channel,在net/Channel.h文件中

/// A selectable I/O channel.////// This class doesn't own the file descriptor./// The file descriptor could be a socket,/// an eventfd, a timerfd, or a signalfdclass 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://更新在poll中  void update();//处理事件    void handleEventWithGuard(Timestamp receiveTime);  //代表不同事件的整数  static const int kNoneEvent;  static const int kReadEvent;  static const int kWriteEvent;  //所属的EventLoop  EventLoop* loop_;//描述符  const int  fd_;//监听的事件  int        events_;  //活跃的事件  int        revents_;//在poll类中的下标,初始化为-1  int        index_; // used by Poller.    bool       logHup_;  //保存弱引用  boost::weak_ptr<void> tie_;//是否获得弱引用  bool tied_;//是否处于处理事件的函数中  bool eventHandling_;//事件的回调函数  ReadEventCallback readCallback_;  EventCallback writeCallback_;  EventCallback closeCallback_;  EventCallback errorCallback_;};

主要属性就是文件描述符,处理所关心事件的函数指针,所监听的事件,活跃的事件,所属的EventLoop,以及一些属性的设置获取方法。

net/Channel.cc

const int Channel::kNoneEvent = 0;const int Channel::kReadEvent = POLLIN | POLLPRI;const int Channel::kWriteEvent = POLLOUT;Channel::Channel(EventLoop* loop, int fd__)  : loop_(loop),    fd_(fd__),    events_(0),    revents_(0),    index_(-1),    logHup_(true),    tied_(false),    eventHandling_(false){}Channel::~Channel(){  assert(!eventHandling_);}//获得所属TcpConnection的弱引用void Channel::tie(const boost::shared_ptr<void>& obj){  tie_ = obj;  tied_ = true;}//修改在poller中的该channal,如果poller中的有关集合中还为由此描述符,则添加到其中//最终调用了所属loop中的poll对象的update函数void Channel::update(){  loop_->updateChannel(this);}//删除在poller集合中于此相同的channal,即不在被poll监听void Channel::remove(){  assert(isNoneEvent());  loop_->removeChannel(this);}//处理事件,在pool循环中,活跃的描述符被遍历调用该各自的回调函数void Channel::handleEvent(Timestamp receiveTime){  boost::shared_ptr<void> guard;  if (tied_)  {  //将弱引用提升,TcpConnection的对象的引用计数+1,变为2    guard = tie_.lock();if (guard)    {      handleEventWithGuard(receiveTime);    }//引用计数变为2  }  else  {    handleEventWithGuard(receiveTime);  }  //guard销毁,TcpConnection引用计数减一,变为1}//根据revents_调用不同的回调函数void Channel::handleEventWithGuard(Timestamp receiveTime){//表示正在调用该描述符的事件处理的回调函数//在析构若该函数还在执行,则出错  eventHandling_ = true;//注意//服务端主动关闭,客户端read()返回0,关闭连接,服务端会同时接受到POLLHUP和POLLIN//而如果是客户端主动断开连接,则服务端只会接收到POLLIN  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))  {    if (logHup_)    {      LOG_WARN << "Channel::handle_event() POLLHUP";    }    if (closeCallback_) closeCallback_();  }  if (revents_ & POLLNVAL)  {    LOG_WARN << "Channel::handle_event() POLLNVAL";  }  if (revents_ & (POLLERR | POLLNVAL))  {    if (errorCallback_) errorCallback_();  }  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))  {    if (readCallback_) readCallback_(receiveTime);  }  if (revents_ & POLLOUT)  {    if (writeCallback_) writeCallback_();  }  eventHandling_ = false;}//打印,在loop中可输出日志string Channel::reventsToString() const{  std::ostringstream oss;  oss << fd_ << ": ";  if (revents_ & POLLIN)    oss << "IN ";  if (revents_ & POLLPRI)    oss << "PRI ";  if (revents_ & POLLOUT)    oss << "OUT ";  if (revents_ & POLLHUP)    oss << "HUP ";  if (revents_ & POLLRDHUP)    oss << "RDHUP ";  if (revents_ & POLLERR)    oss << "ERR ";  if (revents_ & POLLNVAL)    oss << "NVAL ";  return oss.str().c_str();

其中主要的就是handleEvent函数,该函数调用了handleEventWithGuard函数,会根据活跃事件的类型调用不同的函数。在EventLoop的循环loop()中,就是在poll返回活跃的Channel后,遍历调用其handleEvent函数。


接下来看真正的IO复用的封装。首先是一个抽象类Poller

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;  Poller(EventLoop* loop);  virtual ~Poller();//  /// Polls the I/O events.  /// Must be called in the loop thread.  //调用epoll/poll,将活跃的事件存放到activeChannels中  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;  /// Changes the interested I/O events.  /// Must be called in the loop thread.  //¸更新channel  virtual void updateChannel(Channel* channel) = 0;  /// Remove the channel, when it destructs.  /// Must be called in the loop thread.  //移除channel  virtual void removeChannel(Channel* channel) = 0;  static Poller* newDefaultPoller(EventLoop* loop);  void assertInLoopThread()  {    ownerLoop_->assertInLoopThread();  } private: //所属的Eventloop  EventLoop* ownerLoop_;};

该类定义了几个虚函数,poll函数的具体实现类 net/Poller/PollPoller.h

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_;  //存放channel *的map  ChannelMap channels_;};
主要是存储容器,在PollPoller类具体实现中,首先看一下poll的具体实现

Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels){  // XXX pollfds_ shouldn't change //pollfd_  ÀàÐÍΪvector<struct pollfd>,  //调用poll()函数  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);  Timestamp now(Timestamp::now());  if (numEvents > 0)  {    LOG_TRACE << numEvents << " events happended";//将pollfds中活跃的描述符对于的Channel,添加到activeChannels中    fillActiveChannels(numEvents, activeChannels);  }  else if (numEvents == 0)  {    LOG_TRACE << " nothing happended";  }  else  {    LOG_SYSERR << "PollPoller::poll()";  }  return now;}
调用poll,之后调用fillActiveChannels,fillActiveChannels实现如下:

//将活跃的描述符对于的channel添加到activeChannels中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;      ChannelMap::const_iterator ch = channels_.find(pfd->fd);//找到对应的channel      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);//添加到activeChannels    }  }}

removeChannel的实现如下

//移除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());//获得对应的pollfds_中的下标   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());  //移除map  size_t n = channels_.erase(channel->fd());    assert(n == 1); (void)n;  //如果是在pollfds_的最后一个元素,直接移除  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)  {    pollfds_.pop_back();  }  //如果移除的元素不在pollfds_即vector的末尾,则先与末尾元素交换顺序,在移除末尾元素  else  {  //获得末尾的元素的fd    int channelAtEnd = pollfds_.back().fd;//交换    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);    if (channelAtEnd < 0)    {      channelAtEnd = -channelAtEnd-1;    }//设置index    channels_[channelAtEnd]->set_index(idx);    pollfds_.pop_back();  }
移除channel时,需要从map和vector中移除。从map中移除比较容易,而从vector中移除是,为了提高效率,首先channel中的index记录了描述符对应的struct pollfd在pollfds_中的位置,以便于快速找到要移除的pollfd,之后,如果要移除的元素在pollfds_末尾,则直接移除,否则,先与末尾元素交换位置后再移除末尾元素,这与vector底层是数组的有关。

updateChannel实现:

//¸更新map和vectorvoid PollPoller::updateChannel(Channel* channel){  Poller::assertInLoopThread();    LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();//index<0,说明该描述符还未添加到pollfds_ 中,执行添加操作  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);//保存到map    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;    }  }}

该函数狐区分是添加描述符还是修改描述非的监听事件。


epoll的实现类如下:

class EPollPoller : public Poller{ public:  EPollPoller(EventLoop* loop);  virtual ~EPollPoller();  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);  virtual void updateChannel(Channel* channel);  virtual void removeChannel(Channel* channel); private: //  static const int kInitEventListSize = 16;  void fillActiveChannels(int numEvents,                          ChannelList* activeChannels) const;  void update(int operation, Channel* channel);  typedef std::vector<struct epoll_event> EventList;  typedef std::map<int, Channel*> ChannelMap;  //epoll_ctl的返回值  int epollfd_;  //调用epoll_wait得到的活跃描述符数组  EventList events_;    //存放fd,channel的map  ChannelMap channels_;};
也是有存放channel的map,poll函数实现如下:

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){//调用epoll_wait  int numEvents = ::epoll_wait(epollfd_,                               &*events_.begin(),                               static_cast<int>(events_.size()),                               timeoutMs);  Timestamp now(Timestamp::now());  if (numEvents > 0)  {    LOG_TRACE << numEvents << " events happended";//将描述符对应的 channel添加到activeChannels中    fillActiveChannels(numEvents, activeChannels);//扩大容量    if (implicit_cast<size_t>(numEvents) == events_.size())    {      events_.resize(events_.size()*2);    }  }  else if (numEvents == 0)  {    LOG_TRACE << " nothing happended";  }  else  {    LOG_SYSERR << "EPollPoller::poll()";  }  //  return now;}
fillActiveChannels如下

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    channel->set_revents(events_[i].events);    activeChannels->push_back(channel); //添加  }}


removeChannel实现如下:

void EPollPoller::removeChannel(Channel* channel){  Poller::assertInLoopThread();    //»ñÈ¡¸Ãchannal¶ÔÓ¦µÄÃèÊö·û  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);}
因为内核管理的文件描述符和struct event,因此执行从map中移除对应的channel,updateChannel实现如下:

void EPollPoller::updateChannel(Channel* channel){  Poller::assertInLoopThread();  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();  //index用于区分是否是新的描述符  const int index = channel->index();  if (index == kNew || index == kDeleted)  {    // 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); //添加到epoll中  }  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())    {      update(EPOLL_CTL_DEL, channel);  //调用updata      channel->set_index(kDeleted);   //移除    }    else    {      update(EPOLL_CTL_MOD, channel);    }  }}

uodata函数如下:

void EPollPoller::update(int operation, Channel* channel){  struct epoll_event event;  bzero(&event, sizeof event);  event.events = channel->events();    event.data.ptr = channel;  int fd = channel->fd();    if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)  {    if (operation == EPOLL_CTL_DEL)    {      LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd;    }    else    {      LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd;    }  }}
根据operation调用epoll_ctl函数.

在默认情况下,有

Poller* Poller::newDefaultPoller(EventLoop* loop){  if (::getenv("MUDUO_USE_POLL"))  {    return new PollPoller(loop);  }  else  {    return new EPollPoller(loop);  }}
该函数自动选择使用epoll的类。


总的来说,用户执行调用poll函数,该函数将活跃的描述符对应的Channel存放在vector容器中返回,然后循环遍历每一个 channel,调用其handleEvent即可。




















原创粉丝点击