Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)

来源:互联网 发布:linux搭建局域网 编辑:程序博客网 时间:2024/05/22 12:18

转自:http://blog.csdn.net/nk_test/article/details/51052539


(一)TCP网络编程的本质:三个半事件

1. 连接的建立,包括服务端接受(accept) 新连接和客户端成功发起(connect) 连接。TCP 连接一旦建立,客户端和服务端是平等的,可以各自收发数据。

2. 连接的断开,包括主动断开(close 或shutdown) 和被动断开(read(2) 返回0)。

3. 消息到达,文件描述符可读。这是最为重要的一个事件,对它的处理方式决定了网络编程的风格(阻塞还是非阻塞,如何处理分包,应用层的缓冲如何设计等等)。

3.5 消息发送完毕,这算半个。对于低流量的服务,可以不必关心这个事件;另外,这里“发送完毕”是指将数据写入操作系统的缓冲区,将由TCP 协议栈负责数据的发送与重传,不代表对方已经收到数据。


这其中,最主要的便是第三点: 消息到达,文件描述符可读。下面我们来仔细分析(顺便分析消息发送完毕):


(1)消息到达,文件可读:

内核接收-> 网络库可读事件触发--> 将数据从内核转至应用缓冲区(并且回调函数OnMessage根据协议判断是否是完整的数据包,如果不是立即返回)-->如果完整就取出读走、解包、处理、发送(read decode compute encode write)

(2)消息发送完毕:

应用缓冲区-->内核缓冲区(可全填)--->触发发送完成的事件,回调Onwrite。如果内核缓冲区不足以容纳数据(高流量的服务),要把数据追加到应用层发送缓冲区中内核数据发送之后,触发socket可写事件,应用层-->内核;当全发送至内核时,又会回调Onwrite(可继续写)

(二)事件循环类图


EventLoop类:

EventLoop是对Reactor模式的封装,由于Muduo的并发原型是 Multiple reactors + threadpool  (one loop per thread + threadpool),所以每个线程最多只能有一个EventLoop对象。EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL),EventLoop类的构造函数会记录本对象所属线程(threadld_),创建了EventLoop对象的线程称为IO线程,其功能是运行事件循环(EventLooploop),啥也不干==

下面是简化版的EventLoop(内部的Poller尚未实现,只是一个框架)

EventLoop.h

[cpp] view plain copy
  1. #ifndef MUDUO_NET_EVENTLOOP_H  
  2. #define MUDUO_NET_EVENTLOOP_H  
  3.   
  4. #include <boost/noncopyable.hpp>  
  5. #include <muduo/base/CurrentThread.h>  
  6. #include <muduo/base/Thread.h>  
  7.   
  8. namespace muduo  
  9. {  
  10. namespace net  
  11. {  
  12. /// Reactor, at most one per thread.  
  13. /// This is an interface class, so don't expose too much details.  
  14. class EventLoop : boost::noncopyable  
  15. {  
  16.  public:  
  17.   EventLoop();  
  18.   ~EventLoop();  // force out-line dtor, for scoped_ptr members.  
  19.   /// Loops forever.  
  20.   /// Must be called in the same thread as creation of the object.  
  21.   void loop();  
  22.   void assertInLoopThread()  
  23.   {  
  24.     if (!isInLoopThread())  
  25.     {  
  26.       abortNotInLoopThread();  
  27.     }  
  28.   }  
  29.   bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }  
  30.   
  31.   static EventLoop* getEventLoopOfCurrentThread();  
  32.   
  33.  private:  
  34.   void abortNotInLoopThread();  
  35.     
  36.   bool looping_; /* atomic */  
  37.   const pid_t threadId_;        // 当前对象所属线程ID  
  38. };  
  39.   
  40. }  
  41. }  
  42. #endif  // MUDUO_NET_EVENTLOOP_H  


EventLoop.c

[cpp] view plain copy
  1. #include <muduo/net/EventLoop.h>  
  2. #include <muduo/base/Logging.h>  
  3. #include <poll.h>  
  4. using namespace muduo;  
  5. using namespace muduo::net;  
  6.   
  7. namespace  
  8. {  
  9. // 当前线程EventLoop对象指针  
  10. // 线程局部存储  
  11. __thread EventLoop* t_loopInThisThread = 0;  
  12. }  
  13.   
  14. EventLoop* EventLoop::getEventLoopOfCurrentThread()  
  15. {  
  16.   return t_loopInThisThread;  
  17. }  
  18.   
  19. EventLoop::EventLoop()  
  20.   : looping_(false),  
  21.     threadId_(CurrentThread::tid())  
  22. {  
  23.   LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_;  
  24.   // 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL)  
  25.   if (t_loopInThisThread)  
  26.   {  
  27.     LOG_FATAL << "Another EventLoop " << t_loopInThisThread  
  28.               << " exists in this thread " << threadId_;  
  29.   }  
  30.   else  
  31.   {  
  32.     t_loopInThisThread = this;  
  33.   }  
  34. }  
  35.   
  36. EventLoop::~EventLoop()  
  37. {  
  38.   t_loopInThisThread = NULL;  
  39. }  
  40.   
  41. // 事件循环,该函数不能跨线程调用  
  42. // 只能在创建该对象的线程中调用  
  43. void EventLoop::loop()  
  44. {  
  45.   assert(!looping_);  
  46.   // 断言当前处于创建该对象的线程中  
  47.   assertInLoopThread();  
  48.   looping_ = true;  
  49.   LOG_TRACE << "EventLoop " << this << " start looping";  
  50.   
  51.   ::poll(NULL, 0, 5*1000);  
  52.   
  53.   LOG_TRACE << "EventLoop " << this << " stop looping";  
  54.   looping_ = false;  
  55. }  
  56.   
  57. void EventLoop::abortNotInLoopThread()  
  58. {  
  59.   LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this  
  60.             << " was created in threadId_ = " << threadId_  
  61.             << ", current thread id = " <<  CurrentThread::tid();  
  62. }  

Poller类:

时序图:



Poller是个抽象类,具体可以是EPollPoller(默认) 或者PollPoller,需要去实现(唯一使用面向对象的一个类)

对于PollPoller来说,存在一个map,用来关联fd和channel的,我们可以根据fd快速找到对应的channel。一个fd对应一个struct pollfd(pollfd.fd),一个fd 对应一个channel*;这个fd 可以是socket, eventfd, timerfd, signalfd。

Poller的作用是更新IO复用中的channel(IO事件),添加、删除Channel。我们看一下PollPoller的实现:

PollPoller.h

[cpp] view plain copy
  1. #ifndef MUDUO_NET_POLLER_POLLPOLLER_H  
  2. #define MUDUO_NET_POLLER_POLLPOLLER_H  
  3.   
  4. #include <muduo/net/Poller.h>  
  5. #include <map>  
  6. #include <vector>  
  7.   
  8. struct pollfd;  
  9.   
  10. namespace muduo  
  11. {  
  12. namespace net  
  13. {  
  14.   
  15. /// IO Multiplexing with poll(2).  
  16. class PollPoller : public Poller  
  17. {  
  18.  public:  
  19.   
  20.   PollPoller(EventLoop* loop);  
  21.   virtual ~PollPoller();  
  22.   
  23.   virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);  
  24.   virtual void updateChannel(Channel* channel);  
  25.   virtual void removeChannel(Channel* channel);  
  26.   
  27.  private:  
  28.   void fillActiveChannels(int numEvents,  
  29.                           ChannelList* activeChannels) const;  
  30.   
  31.   typedef std::vector<struct pollfd> PollFdList;  
  32.   typedef std::map<int, Channel*> ChannelMap; // key是文件描述符,value是Channel*  
  33.   PollFdList pollfds_;  
  34.   ChannelMap channels_;  
  35. };  
  36.   
  37. }  
  38. }  
  39. #endif  // MUDUO_NET_POLLER_POLLPOLLER_H  

PollPoller.c

[cpp] view plain copy
  1. #include <muduo/net/poller/PollPoller.h>  
  2. #include <muduo/base/Logging.h>  
  3. #include <muduo/base/Types.h>  
  4. #include <muduo/net/Channel.h>  
  5. #include <assert.h>  
  6. #include <poll.h>  
  7.   
  8. using namespace muduo;  
  9. using namespace muduo::net;  
  10.   
  11. PollPoller::PollPoller(EventLoop* loop)  
  12.   : Poller(loop)  
  13. {  
  14. }  
  15.   
  16. PollPoller::~PollPoller()  
  17. {  
  18. }  
  19.   
  20. Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)  
  21. {  
  22.   // XXX pollfds_ shouldn't change  
  23.   int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);  
  24.   Timestamp now(Timestamp::now());  
  25.   if (numEvents > 0)  
  26.   {  
  27.     LOG_TRACE << numEvents << " events happended";  
  28.     fillActiveChannels(numEvents, activeChannels);  
  29.   }  
  30.   else if (numEvents == 0)  
  31.   {  
  32.     LOG_TRACE << " nothing happended";  
  33.   }  
  34.   else  
  35.   {  
  36.     LOG_SYSERR << "PollPoller::poll()";  
  37.   }  
  38.   return now;  
  39. }  
  40.   
  41. void PollPoller::fillActiveChannels(int numEvents,  
  42.                                     ChannelList* activeChannels) const  
  43. {  
  44.   for (PollFdList::const_iterator pfd = pollfds_.begin();  
  45.       pfd != pollfds_.end() && numEvents > 0; ++pfd)  
  46.   {  
  47.     if (pfd->revents > 0)  
  48.     {  
  49.       --numEvents;  
  50.       ChannelMap::const_iterator ch = channels_.find(pfd->fd);  
  51.       assert(ch != channels_.end());  
  52.       Channel* channel = ch->second;  
  53.       assert(channel->fd() == pfd->fd);  
  54.       channel->set_revents(pfd->revents);  
  55.       // pfd->revents = 0;  
  56.       activeChannels->push_back(channel);  
  57.     }  
  58.   }  
  59. }  
  60.   
  61. void PollPoller::updateChannel(Channel* channel)  
  62. {  
  63.   Poller::assertInLoopThread();  
  64.   LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();  
  65.   if (channel->index() < 0)  
  66.   {  
  67.     // index < 0说明是一个新的通道  
  68.     // a new one, add to pollfds_  
  69.     assert(channels_.find(channel->fd()) == channels_.end());  
  70.     struct pollfd pfd;  
  71.     pfd.fd = channel->fd();  
  72.     pfd.events = static_cast<short>(channel->events());  
  73.     pfd.revents = 0;  
  74.     pollfds_.push_back(pfd);  
  75.     int idx = static_cast<int>(pollfds_.size())-1;  
  76.     channel->set_index(idx);  
  77.     channels_[pfd.fd] = channel;  
  78.   }  
  79.   else  
  80.   {  
  81.     // update existing one  
  82.     assert(channels_.find(channel->fd()) != channels_.end());  
  83.     assert(channels_[channel->fd()] == channel);  
  84.     int idx = channel->index();  
  85.     assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));  
  86.     struct pollfd& pfd = pollfds_[idx];  
  87.     assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);  
  88.     pfd.events = static_cast<short>(channel->events());  
  89.     pfd.revents = 0;  
  90.     // 将一个通道暂时更改为不关注事件,但不从Poller中移除该通道  
  91.     if (channel->isNoneEvent())  
  92.     {  
  93.       // ignore this pollfd  
  94.       // 暂时忽略该文件描述符的事件  
  95.       // 这里pfd.fd 可以直接设置为-1  
  96.       pfd.fd = -channel->fd()-1; // 这样子设置是为了removeChannel优化  
  97.     }  
  98.   }  
  99. }  
  100.   
  101. void PollPoller::removeChannel(Channel* channel)  
  102. {  
  103.   Poller::assertInLoopThread();  
  104.   LOG_TRACE << "fd = " << channel->fd();  
  105.   assert(channels_.find(channel->fd()) != channels_.end());  
  106.   assert(channels_[channel->fd()] == channel);  
  107.   assert(channel->isNoneEvent());  
  108.   int idx = channel->index();  
  109.   assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));  
  110.   const struct pollfd& pfd = pollfds_[idx]; (void)pfd;  
  111.   assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());  
  112.   size_t n = channels_.erase(channel->fd());  
  113.   assert(n == 1); (void)n;  
  114.   if (implicit_cast<size_t>(idx) == pollfds_.size()-1)  
  115.   {  
  116.     pollfds_.pop_back();  
  117.   }  
  118.   else  
  119.   {  
  120.     // 这里移除的算法复杂度是O(1),将待删除元素与最后一个元素交换再pop_back  
  121.     int channelAtEnd = pollfds_.back().fd;  
  122.     iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);  
  123.     if (channelAtEnd < 0)  
  124.     {  
  125.       channelAtEnd = -channelAtEnd-1;  
  126.     }  
  127.     channels_[channelAtEnd]->set_index(idx);  
  128.     pollfds_.pop_back();  
  129.   }  
  130. }  
代码中的几个技巧都在注释中标出。

Channel类:

Channel是selectable IO channel,负责注册与响应IO 事件,它不拥有file descriptor。

Channel是Reactor结构中的“事件”,它自始至终都属于一个EventLoop(一个EventLoop对应多个Channel,处理多个IO),负责一个文件描述符的IO事件,它包含又文件描述符fd_,但实际上它不拥有fd_,不用负责将其关闭。在Channel类中保存这IO事件的类型以及对应的回调函数,当IO事件发生时,最终会调用到Channel类中的回调函数。Channel类一般不单独使用,它常常包含在其他类中(Acceptor、Connector、EventLoop、TimerQueue、TcpConnection)使用。Channel类有EventLoop的指针 loop_,通过这个指针可以向EventLoop中添加当前Channel事件。事件类型用events_表示,不同事件类型对应不同回调函数。

以下两个都由Channel注册:

Acceptor是被动连接的抽象--->关注监听套接字的可读事件,回调handleRead

Connector对主动连接的抽象。

时序图:


Channel.h

[cpp] view plain copy
  1. #ifndef MUDUO_NET_CHANNEL_H  
  2. #define MUDUO_NET_CHANNEL_H  
  3.   
  4. #include <boost/function.hpp>  
  5. #include <boost/noncopyable.hpp>  
  6. #include <boost/shared_ptr.hpp>  
  7. #include <boost/weak_ptr.hpp>  
  8. #include <muduo/base/Timestamp.h>  
  9.   
  10. namespace muduo  
  11. {  
  12. namespace net  
  13. {  
  14.   
  15. class EventLoop;  
  16.   
  17. /// A selectable I/O channel.  
  18. /// This class doesn't own the file descriptor.  
  19. /// The file descriptor could be a socket,  
  20. /// an eventfd, a timerfd, or a signalfd  
  21. class Channel : boost::noncopyable  
  22. {  
  23.  public:  
  24.   typedef boost::function<void()> EventCallback;  
  25.   typedef boost::function<void(Timestamp)> ReadEventCallback;  
  26.   
  27.   Channel(EventLoop* loop, int fd);  
  28.   ~Channel();  
  29.   
  30.   void handleEvent(Timestamp receiveTime);  
  31.   void setReadCallback(const ReadEventCallback& cb)  
  32.   { readCallback_ = cb; }  
  33.   void setWriteCallback(const EventCallback& cb)  
  34.   { writeCallback_ = cb; }  
  35.   void setCloseCallback(const EventCallback& cb)  
  36.   { closeCallback_ = cb; }  
  37.   void setErrorCallback(const EventCallback& cb)  
  38.   { errorCallback_ = cb; }  
  39.   
  40.   /// Tie this channel to the owner object managed by shared_ptr,  
  41.   /// prevent the owner object being destroyed in handleEvent.  
  42.   void tie(const boost::shared_ptr<void>&);  
  43.   
  44.   int fd() const { return fd_; }  
  45.   int events() const { return events_; }  
  46.   void set_revents(int revt) { revents_ = revt; } // used by pollers  
  47.   // int revents() const { return revents_; }  
  48.   bool isNoneEvent() const { return events_ == kNoneEvent; }  
  49.   
  50.   void enableReading() { events_ |= kReadEvent; update(); }  
  51.   // void disableReading() { events_ &= ~kReadEvent; update(); }  
  52.   void enableWriting() { events_ |= kWriteEvent; update(); }  
  53.   void disableWriting() { events_ &= ~kWriteEvent; update(); }  
  54.   void disableAll() { events_ = kNoneEvent; update(); }  
  55.   bool isWriting() const { return events_ & kWriteEvent; }  
  56.   
  57.   // for Poller  
  58.   int index() { return index_; }  
  59.   void set_index(int idx) { index_ = idx; }  
  60.   
  61.   // for debug  
  62.   string reventsToString() const;  
  63.   
  64.   void doNotLogHup() { logHup_ = false; }  
  65.   
  66.   EventLoop* ownerLoop() { return loop_; }  
  67.   void remove();  
  68.   
  69.  private:  
  70.   void update();  
  71.   void handleEventWithGuard(Timestamp receiveTime);  
  72.   
  73.   static const int kNoneEvent;  
  74.   static const int kReadEvent;  
  75.   static const int kWriteEvent;  
  76.   
  77.   EventLoop* loop_;         // 所属EventLoop  
  78.   const int  fd_;           // 文件描述符,但不负责关闭该文件描述符  
  79.   int        events_;       // 关注的事件  
  80.   int        revents_;      // poll/epoll返回的事件  
  81.   int        index_;        // used by Poller.表示在poll的事件数组中的序号  
  82.   bool       logHup_;       // for POLLHUP  
  83.   
  84.   boost::weak_ptr<void> tie_;  
  85.   bool tied_;  
  86.   bool eventHandling_;      // 是否处于处理事件中  
  87.   ReadEventCallback readCallback_;  
  88.   EventCallback writeCallback_;  
  89.   EventCallback closeCallback_;  
  90.   EventCallback errorCallback_;  
  91. };  
  92.   
  93. }  
  94. }  
  95. #endif  // MUDUO_NET_CHANNEL_H  
Channel.c

[cpp] view plain copy
  1. #include <muduo/base/Logging.h>  
  2. #include <muduo/net/Channel.h>  
  3. #include <muduo/net/EventLoop.h>  
  4. #include <sstream>  
  5. #include <poll.h>  
  6.   
  7. using namespace muduo;  
  8. using namespace muduo::net;  
  9.   
  10. const int Channel::kNoneEvent = 0;  
  11. const int Channel::kReadEvent = POLLIN | POLLPRI;  
  12. const int Channel::kWriteEvent = POLLOUT;  
  13.   
  14. Channel::Channel(EventLoop* loop, int fd__)  
  15.   : loop_(loop),  
  16.     fd_(fd__),  
  17.     events_(0),  
  18.     revents_(0),  
  19.     index_(-1),  
  20.     logHup_(true),  
  21.     tied_(false),  
  22.     eventHandling_(false)  
  23. {  
  24. }  
  25.   
  26. Channel::~Channel()  
  27. {  
  28.   assert(!eventHandling_);  
  29. }  
  30.   
  31. void Channel::tie(const boost::shared_ptr<void>& obj)  
  32. {  
  33.   tie_ = obj;  
  34.   tied_ = true;  
  35. }  
  36.   
  37. void Channel::update()  
  38. {  
  39.   loop_->updateChannel(this);  
  40. }  
  41.   
  42. // 调用这个函数之前确保调用disableAll  
  43. void Channel::remove()  
  44. {  
  45.   assert(isNoneEvent());  
  46.   loop_->removeChannel(this);  
  47. }  
  48.   
  49. void Channel::handleEvent(Timestamp receiveTime)  
  50. {  
  51.   boost::shared_ptr<void> guard;  
  52.   if (tied_)  
  53.   {  
  54.     guard = tie_.lock();  
  55.     if (guard)  
  56.     {  
  57.       handleEventWithGuard(receiveTime);  
  58.     }  
  59.   }  
  60.   else  
  61.   {  
  62.     handleEventWithGuard(receiveTime);  
  63.   }  
  64. }  
  65.   
  66. void Channel::handleEventWithGuard(Timestamp receiveTime)  
  67. {  
  68.   eventHandling_ = true;  
  69.   if ((revents_ & POLLHUP) && !(revents_ & POLLIN))  
  70.   {  
  71.     if (logHup_)  
  72.     {  
  73.       LOG_WARN << "Channel::handle_event() POLLHUP";  
  74.     }  
  75.     if (closeCallback_) closeCallback_();  
  76.   }  
  77.   
  78.   if (revents_ & POLLNVAL)  
  79.   {  
  80.     LOG_WARN << "Channel::handle_event() POLLNVAL";  
  81.   }  
  82.   
  83.   if (revents_ & (POLLERR | POLLNVAL))  
  84.   {  
  85.     if (errorCallback_) errorCallback_();  
  86.   }  
  87.   if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))  
  88.   {  
  89.     if (readCallback_) readCallback_(receiveTime);  
  90.   }  
  91.   if (revents_ & POLLOUT)  
  92.   {  
  93.     if (writeCallback_) writeCallback_();  
  94.   }  
  95.   eventHandling_ = false;  
  96. }  
  97.   
  98. string Channel::reventsToString() const  
  99. {  
  100.   std::ostringstream oss;  
  101.   oss << fd_ << ": ";  
  102.   if (revents_ & POLLIN)  
  103.     oss << "IN ";  
  104.   if (revents_ & POLLPRI)  
  105.     oss << "PRI ";  
  106.   if (revents_ & POLLOUT)  
  107.     oss << "OUT ";  
  108.   if (revents_ & POLLHUP)  
  109.     oss << "HUP ";  
  110.   if (revents_ & POLLRDHUP)  
  111.     oss << "RDHUP ";  
  112.   if (revents_ & POLLERR)  
  113.     oss << "ERR ";  
  114.   if (revents_ & POLLNVAL)  
  115.     oss << "NVAL ";  
  116.   
  117.   return oss.str().c_str();  
  118. }  
这三个类之间的关系不难理解,其实本质就是一个Poll/Epoll,只不过进行了更高的抽象后划分出来的这些类,重点理解博客开头的那张类图即可。

参考:

《Muduo使用手册》

《linux多线程服务端编程》

阅读全文
0 0
原创粉丝点击