文档章节

Muduo网络库分析(三)

Fire_thief
 Fire_thief
发布于 2014/06/06 13:49
字数 1468
阅读 118
收藏 2

    本篇主要讲多路复用IO接口以及对定时器的实现。在void EventLoop::loop()中调用多路复用IO接口的代码是:

pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);多路复用IO接口被封装成类Poller

class Poller : boost::noncopyable
{
 public:
  typedef std::vector<Channel*> ChannelList;//注册到多路复用IO接口的Channel

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


  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;//阻塞等待事件触发,或者超时


  virtual void updateChannel(Channel* channel) = 0;//更新Channel的注册状态

  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  virtual void removeChannel(Channel* channel) = 0;//将Channel从监听队列中删除

  static Poller* newDefaultPoller(EventLoop* loop);//获取默认的多路复用IO接口可能是poll可能是epoll

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

 private:
  EventLoop* ownerLoop_;
};

Poller本身只提供一些借口。真正实现功能的还是PollPoller和EpollPoller也解释poll和epoll。我们重点分析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;

  int epollfd_;
  EventList events_;//用于存放激活的epoll事件
  ChannelMap channels_;
};

    下面是EpollPoller类实现的poll方法:

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  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 happended";
    fillActiveChannels(numEvents, activeChannels);//将活动事件对应的Channel加入到活动队列中去。
    if (implicit_cast<size_t>(numEvents) == events_.size())
    {
      events_.resize(events_.size()*2);
    }
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << " nothing happended";
  }
  else
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;
}

回头看loop()方法中可以知道在当所有活动Channel存入到队列中之后就将一次调用Channel的回调函数处理相应的请求。真个过程是比较常见的处理方式,我个人认为这一部分除了对超时事件需要仔细分析其它的都是可以卡巴代码就一目了然的。当然主要还是要理清封装的层次以及epoll事件和Channel类之间的封装关系,这是理解额度关键。

    对于超时事件的处理库的作者是面向比较新的版本的linux设计的。作者利用新版linux提供的timerfd_create()函数创建了一个文件描述符用于描述定时器。这样定时事件到来时就会通过对应的文件描述符检测到。这样就将定时器和IO事件同等地放入多路IO复用接口处理。这归功于新版linux停提供这个接口,对于版本比较老或者强调兼容性来说,经典的做法是将定时器和多路IO复用接口的超时返回进行有效的结合,libevent就是这么实现的。

关于定时器的文档可以参看http://cpp.ezbty.org/import_doc/linux_manpage/timerfd_gettime.2.html

Muduo的定时器主要涉及的类:
    Timer
    TimerQueue
    Timestamp
    TimeZon

    Timer主要维护了定时器到时间需要处理的具体任务,Timestamp主要维护了定时器超时的具体时间。TimerQueue则主要维护了定时器队列(set)。一个IO线程有一个定时器队列对象,即一个TimerQueue对象,这些定时器都被这个TimerQueue对象中的timerfd_描述(timerfd_create()函数创建),在TimerQueue中已经将timerfd_封装成Channel注册到IO多路复用接口中了,这使得io事件和定时器事件统一了起来。当多路复用接口监测到timerfd_有事件,说明有定时器超时了,这时就从定时器队列中找出超时定时器,并处理相关回调函数操作。

    定时器的队列设计作者使用了std::set,由于set是通过红黑树实现所以在删除和添加元素时性能较好。鉴于此set中存放的对象必须重载操作符<。在TimerQueue中set存储的对象是一个std::pair<Timestamp, Timer*>。只要Timestamp能够重载<操作符就可以了。具体代码就不在此分析了,希望有兴趣的可以参看代码。


到现在Muduo的简单分析就结束了,如果需要详细理解看代码是必不可少的。代码中的许多细节是值得我们学习的,比如智能指针以及非阻塞型的connect的实现都是一些很靠得参考。当然我对作者的谢谢代码还是存有异议的,比如在获取超时定时器的函数:std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now);

我不建议将一个复杂的类型作为一个函数返回值,而我更愿意这样:

int TimerQueue::getExpired(Timestamp now, std::vector<TimerQueue::Entry> & outtime_list);

这样减少了内存复制的次数,特别想使用容器时,也减少了容器内部的操作次数。

为了直观的感受可以运行下面的代码:

#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>

class testclass
{
public:
    testclass():i(0){}
    testclass(int m){i = m;}
    ~testclass(){printf("kkk\n");}
private:
    int i;
};



void WithReference(std::vector<testclass>& kk)
{

    testclass l(18);
    testclass m;
    kk.push_back(l);
    kk.push_back(m);

}

void WithPtr(std::vector<testclass>* kk)
{

    testclass l(18);
    testclass m;
    kk->push_back(l);
    kk->push_back(m);

}

std::vector<testclass> WithObject()
{
    std::vector<testclass> temp;
    testclass l(18);
    testclass m;
    temp.push_back(l);
    temp.push_back(m);
    return temp;
}

    int main()
   
    {


        printf("********WithWithReference********\n");
        std::vector<testclass> l0;
        WithReference(l0);
        printf("********WithPtr********\n");
        std::vector<testclass> l1;
        WithPtr(&l1);
        printf("********WithObject********\n");
        std::vector<testclass> l2;
        l2 = WithObject();

    return 0;
   
    }

    希望你运行完能知道我说的是什么。整个Muduo的实现还是不算很复杂,向对于C++编写的库了解流程是一个关键,但是搞清楚各个类之间的封装层次上下关系和包含关系也是关键。像这类以基于回调机制的reactor模式的网络库,寻找到对应的回调函数,跟着回调函数指针走,就能理清流程,即使它很有悖人类的自然思维方式。

© 著作权归作者所有

共有 人打赏支持
Fire_thief
粉丝 1
博文 6
码字总数 7916
作品 0
苏州
高级程序员
收藏的博客 -- 高性能Linux服务器

http://zhuanlan.51cto.com/columnlist/shenj/ --- 58沈剑 http://blog.csdn.net/analogouslove --- 范蠡&张小方 http://blog.csdn.net/column/details/15700.html --- teamtalk https://gi......

libaineu2004
2017/08/08
0
0
【开源访谈】Muduo 作者陈硕访谈实录

关于开源访谈 开源访谈是开源中国推出的一系列针对国内优秀开源软件作者的访谈,以文字的方式记录并传播。我们希望开源访谈能全面的展现国内开源软件、开源软件作者的现状,着实推动国内开源...

虫虫
2012/07/16
26.1K
20
OSChina 第39期有奖高手问答:Linux 多线程服务端编程

OSCHINA 本期高手问答(8月7日-8月13日)我们请来了 @ChenShuo 为大家解答关于 Linux 多线程服务端编程方面:使用muduo C++网络库的问题。 @ChenShuo 陈硕,擅长C++ 多线程网络编程和实时分布式...

红薯
2013/08/07
13.5K
80
Muduo 0.6.0 Beta 发布,C++ 网络库

Muduo 0.6.0 Beta 发布,下载地址:muduo-0.6.0-beta.tar.gz 140 KB 改进记录: Put hostname as part of log file name. Extract muduo/base/CurrentThread.h Optimize logging for thread ......

oschina
2012/06/11
1K
3
Linux C++ 网络库--Muduo

muduo 是一个基于 Reactor 模式的现代 C++ 网络库,它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。视频连接:http://v.youku....

ChenShuo
2012/03/04
26.5K
2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

配置Spring的注解支持

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 配置Spring的注解支持 以上也提到了使用注解来配...

凯哥学堂
28分钟前
0
0
关于Spring Aop存在的一点问题的思考

在本人前面的文章Spring Aop原理之切点表达式解析中讲解了Spring是如何解析切点表达式的,在分析源码的时候,出现了如下将要讲述的问题,我认为是不合理的,后来本人单纯使用aspectj进行试验...

爱宝贝丶
30分钟前
0
0
JavaScript 概述

JavaScript是面向Web的编程语言。绝大多数现代网站都使用了JavaScript,并且所有的现代Web浏览器——基于桌面系统、游戏机、平板电脑和智能手机的浏览器——均包含了JavaScript解释器。这使得...

Mr_ET
今天
0
0
Java Run-Time Data Areas(Java运行时数据区/内存分配)

Java运行时数据区(内存分配) 本文转载官网 更多相关内容可查看官网 中文翻译可参考 2.5. Run-Time Data Areas The Java Virtual Machine defines various run-time data areas that are use...

lichuangnk
今天
0
0
docker learn :services docker-compose.yml

docker-compose.yml定义了服务的运行参数 version: "3" services: web: # replace username/repo:tag with your name and image details image: hub.c.163.com/dog948453219/friendlyhello d......

writeademo
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部