网络编程入门04

Poll函数

#include <poll.h>
  int poll(struct pollfd fd[], nfds_t nfds, int timeout);

    参数:

   1)第一个参数:一个结构数组,struct pollfd结构如下:

  struct pollfd{

  int fd;              //文件描述符

  short events;    //请求的事件

  short revents;   //返回的事件

  };
        2)第二个参数nfds:要监视的描述符的数目。
       3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果  它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

poll函数的事件标志符值
常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据优先级数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

fd_set 与 select 关系先要了解.
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数说明:

  nfds:监听的所有文件描述符中的最大描述符+1(其实内核是轮询查的)

  readfds:读文件描述符监听集合

  writefds:写文件描述符监听集合

  exceptfds:异常文件描述符监听集合

  timeout:有几个值如下:

      大于0:设置监听超时时长

      NULL:阻塞监听

      0:非阻塞监听

  函数返回值:

       大于0:所有监听集合(3个)中,满足对应事件的总数

        0:没有满足的

       -1:出错error

       void FD_CLR(int fd, fd_set *set);      //将一个文件描述符从集合中移除
  int FD_ISSET(int fd, fd_set *set);  //判断一个文件描述符是否在一个集合中,返回值:在 1,不在 0
  void FD_SET(int fd, fd_set *set);  //将监听的文件描述符,添加到监听集合中
  void FD_ZERO(fd_set *set);    //清空一个文件描述符集合
       time_val{
             long tv_sec;          //seconds
             long tv_usec;// microseconds
        }

Channel.h

calss Channel{
public:
  typedef std::function<void()> EventCallback;               //无参数可调用对象 为事件回调
  typedef std::function<void(Timestamp)> ReadEventCallback;  //带有时间戳参数的为 读事件回调
private:
  static const int kNoneEvent = 0            //先定义三个事件常量
  static const int kReadEvent = POLLIN | POLLPRI;
  static const int kWriteEvent = POLLOUT;
  EventLoop* loop_;               //主循环
  const int  fd_;  //监控的文件描述符
  int        events_;  //监控的事件 
  int        revents_; // it's the received event types of epoll or poll  //收到的事件
  int        index_; // used by Poller.        
  bool       logHup_;

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;             
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
public:

  ctr(EventLoop* loop, int fd)  //构造函数
     : loop_(loop),
        fd_(fd__),
        events_(0),
        revents_(0),
        index_(-1),  //出事index为-1  对应的channel在 轮训器中pollfd数组中下标的位置
        logHup_(true),
        tied_(false),
        eventHandling_(false),
        addedToLoop_(false)
      { }

  ~dtr(){     //析构函数 
    assert(!eventHandling_);  //确定当前未处理时间
      assert(!addedToLoop_);  //确定没有加入到某个事件循环
      if(loop_->isInLoopThread())  //如果当前线程为循环所在线程
      {
        assert(!loop_->hasChannel(this)); //确定这个时间没有这个频道
      }
  }

  void handleEvent(Timestamp receiveTime){
     std::shared_ptr<void> guard;
      if (tied_)   //如果有一个弱指针指向了一个类外对象
      {
        guard = tie_.lock();  //将指针提升为引用计数指针,保证在处理事件中,tie_指向的内存不会被析构
        if (guard)
        {
          handleEventWithGuard(receiveTime);
        }
      }
      else
      {
        handleEventWithGuard(receiveTime);
      }

  }

  //设定几个事件的回调函数
  void setReadCallback(ReadEventCallback cb)

  void tie(const std::shared_ptr<void>& obj) {
     tie_ = obj;  //防止obj被析构,用一个弱指针指针帮助不会被析构 
     tied_ = true;
  }
  
  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; }

  bool isReading() const { return events_ & kReadEvent; }
 
  // for Poller
  int index() { return index_; }

  void set_index(int idx) { index_ = idx; }

  // for debug
  string reventsToString() const {
   //调用 eventsToString(int fd, int ev);
  }

  string eventsToString() const {
   //调用 eventsToString(int fd, int ev);
  }

  void doNotLogHup() { logHup_ = false; }

  EventLoop* ownerLoop() { return loop_; }

  void remove() {  //频道对象移除循环对象
       addedToLoop_ = false;
      loop_->removeChannel(this);
  }

private:
  static string eventsToString(int fd, int ev)  {
      //把一些信息以字符串返回
  }

  void update() {       //把频道对象加入循环对象
      addedToLoop_ = true;
      loop_->updateChannel(this); 
  }

  void handleEventWithGuard(Timestamp receiveTime) {
     //有什么样的事件,调用什么样的处理回调处理
  }
}

Poller.h 轮训器基类

class Poller{
protected: //子类可继承
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;   //有一个int到频道指针的映射   文件描述符到频道指针映射
 private:
  EventLoop* ownerLoop_;  //有一个事件循环对象
public:
    typedef std::vector<Channel*> ChannelList;

    Poller(EventLoop* loop); //直接赋值loop的构造器

    virtual ~Poller(); //默认析构器

    virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0; //给一个时间,限制和频道指针列表
 
    virtual void updateChannel(Channel* channel) = 0; //更新频道

    virtual void removeChannel(Channel* channel) = 0;//去除频道

    virtual bool hasChannel(Channel* channel) const; //判断这个轮训器是否有这个频道
   {
      assertInLoopThread();
      ChannelMap::const_iterator it = channels_.find(channel->fd());  //根据频道描述符查找
      return it != channels_.end() && it->second == channel;
    }

    static Poller* newDefaultPoller(EventLoop* loop);//给一个循环对象指针,返回一个一个默认轮训器
   
    void assertInLoopThread() const;//判断当前线程是否持有一个事件循环对象

}

PollPoller.h Poll轮询器

class PollPoller : Poller {
private:
    typedef std::vector<struct pollfd> PollFdList;

    PollFdList pollfds_;   

   void fillActiveChannels(int numEvents,
                                       ChannelList* activeChannels) const {
       for (PollFdList::const_iterator pfd = pollfds_.begin();
                                                      pfd != pollfds_.end() && numEvents > 0; ++pfd)  //遍历这颗红黑树
  {
    if (pfd->revents > 0)  //如果这个pollfd --文件描述符对应的文件有任务就绪
    {
      --numEvents;
      ChannelMap::const_iterator ch = channels_.find(pfd->fd);   //根据文件描述符找到对于的频道指针     
      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);  //加入活跃列表
    }
  }

   }

public:

    PollPoller(EventLoop* loop) {  //直接把指针给父类 简单构造
    }

    ~PollPoller() override;  //默认析构

      Timestamp poll(int timeoutMs, ChannelList* activeChannels) override {
          int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);   //轮询  poll在有事件返回或者超时前会阻塞
          int savedErrno = errno;
          Timestamp now(Timestamp::now());
          if (numEvents > 0)                 //如果有任务
          {
                LOG_TRACE << numEvents << " events happened";
                fillActiveChannels(numEvents, activeChannels);  //如果有任务,就把这些时间加入活跃频道列表
          }
          else if (numEvents == 0)           //如果没有事件就绪
          {
               LOG_TRACE << " nothing happened";
          }
          else                                //出错
          {
                if (savedErrno != EINTR)
                {
                  errno = savedErrno;
                  LOG_SYSERR << "PollPoller::poll()";
                }
          }
          return now;
      }

      void updateChannel(Channel* channel) override {
            Poller::assertInLoopThread();
     
          if (channel->index() < 0)  //这个频道的index为-1,说明没被加入任何轮训器中
          {
            // 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);               //这个频道的index设为轮训器中poolfd数组下标位置
            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.fd = channel->fd();
                pfd.events = static_cast<short>(channel->events());
                pfd.revents = 0;
                if (channel->isNoneEvent())
                {
                  // ignore this pollfd
                  pfd.fd = -channel->fd()-1;
                }
          }


       }
      void removeChannel(Channel* channel) override {
              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         //不需要移动整个数组,只需要和最后一个换一下然后pop_back
              {
                    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();
              }
      } 
}

EPollPoller.h 整体结构和PollPoller很类似

epoll_event结构体一般用在epoll机制中,其定义如下:

struct epoll_event
{
  uint32_t events;   /* Epoll events */
  epoll_data_t data;    /* User data variable */
} __attribute__ ((__packed__));

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;


*const int kNew = -1; *
*const int kAdded = 1;*
*const int kDeleted = 2;*

class EPollPoller : public Poller
{
private:

    typedef std::vector<struct epoll_event> EventList;
    int epollfd_;
    EventList events_;  //epoll_event数组

 public:

/*
int epoll_create1(int flags);
功能:创建一个多路复用的实例
参数:
flags:
0:如果这个参数是0,这个函数等价于poll_create(0)
EPOLL_CLOEXEC:这是这个参数唯一的有效值,如果这个参数设置为这个。那么当进程替换映像的时候会关闭这个文件描述符,这样新的映像中就无法对这个文件描述符操作,适用于多进程编程+映像替换的环境里
返回值:
success:返回一个非0 的未使用过的最小的文件描述符
error:-1 errno被设置
*/

  EPollPoller(EventLoop* loop)
         : Poller(loop),
        epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
        events_(kInitEventListSize)  // 16
        {
             if (epollfd_ < 0)
              {
                LOG_SYSFATAL << "EPollPoller::EPollPoller";
          }
        }

 
  ~EPollPoller() override {
         ::close(epollfd_);
    }

/*
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:等待一个epoll队列中的文件描述符的I/O事件发生
参数:
epfd:目标epoll队列的描述符
events:用于放置epoll队列中准备就绪(被触发)的事件
maxevents:最大事件?
timeout:指定函数等待的时间。这个函数阻塞这么长一段时间之后接触阻塞。
返回值:
>=0,表示准备就绪的文件描述符个数
-1:出错,errno被设置
*/

  Timestamp poll(int timeoutMs, ChannelList* activeChannels) override {
          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 happened";
                fillActiveChannels(numEvents, activeChannels); //加入活跃频道
                if (implicit_cast<size_t>(numEvents) == events_.size())
                {
                  events_.resize(events_.size()*2);
                }
          }
          else if (numEvents == 0)
          {
                LOG_TRACE << "nothing happened";
          }
          else
          {
                // error happens, log uncommon ones
                if (savedErrno != EINTR)
                {
                      errno = savedErrno;
                  LOG_SYSERR << "EPollPoller::poll()";
               }    
          }
          return now;
  }

/*
int epoll_ctl(int epfd, int op, int fd, struct 	epoll_event *event);
RETURN:0,成功;-1,出错
函数描述:

        (1) epfd为epoll_create创建的epoll描述符

        (2) epoll_ctl函数对epoll进行op类型的操作,op选项为

              EPOLL_CTL_ADD,对fd描述符注册event事件

              EPOLL_CTL_MOD,对fd描述符的event事件进行修改

              EPOLL_CTL_DEL,删除已注册的event事件
*/
  void updateChannel(Channel* channel) override {
      Poller::assertInLoopThread();
      const int index = channel->index();
       LOG_TRACE << "fd = " << channel->fd()  << " events = " << channel->events() << " index = " << index;
       if (index == kNew || index == kDeleted)  //如果这个channel  索引为新,活着被删除
      {
            // 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);
       }
       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);
                  channel->set_index(kDeleted);        //设置为被取消关注
            }
            else
            {
                  update(EPOLL_CTL_MOD, channel);
            }
  }

  }
   
  void removeChannel(Channel* channel) override {
         Poller::assertInLoopThread();
          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);           //在事件映射出,删除这个fd 代表轮训器不处理这个频道了
          (void)n;
          assert(n == 1);

          if (index == kAdded)
          {
            update(EPOLL_CTL_DEL, channel);
          }
          channel->set_index(kNew);
  }

 private:
  static const int kInitEventListSize = 16;

  static const char* operationToString(int op);

  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const {
         for (int i = 0; i < numEvents; ++i)  //event_里返回的都是已经就绪的
          {
            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);
          }

  }
  void update(int operation, Channel* channel) {      //对这个频道的事件进行注册或者修改管理
           struct epoll_event event;
          memZero(&event, sizeof event);
          event.events = channel->events();
          event.data.ptr = channel;
          int fd = channel->fd();
          LOG_TRACE << "epoll_ctl op = " << operationToString(operation)  << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
          if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
          {
                if (operation == EPOLL_CTL_DEL)
                {
                      LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
                }
                else
                {
                  LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
                }
          }

  }

  typedef std::vector<struct epoll_event> EventList;

  int epollfd_;
  EventList events_;
};

posted @ 2019-07-26 18:25  just4fun  阅读(172)  评论(0编辑  收藏  举报