webserver服务器从零搭建到上线(八)|EpollPoller变乱分发器类 ...

打印 上一主题 下一主题

主题 562|帖子 562|积分 1686

终于要开始我们的重点:变乱分发起和变乱循环了,在这里我们将揭开变乱驱动的IO多路复用模子的神秘面纱!
EpollPoller变乱分发器类

成员变量和成员函数解释

这些都是在头文件中声明的,我们可以先对类中封装的各个方法进行公道的研究和推测。
私有的成员函数和成员变量

在这里我们简单介绍一下私有成员函数和成员变量
私有成员函数如下
  1. void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
  2. void update(int operation, Channel *channel);
复制代码


  • fillActiveChannels()这里主要就是将 epoll_wait 返回的活跃变乱填充到 activeChannels中。
  • update()这里的根据操纵范例(添加、修改、删除),调用epoll_ctl来更新epoll实例中的Channel对象。
    在Channel类中,我们也写了一个update,它的具体实现是loop_->updateChannel(this);,调用了EventLoop中的updateChannel,所以我们有理由怀疑,其中的updateChannel()就是在调用这里的update方法

私有成员变量
  1. static const int kInitEventListSize = 16;
  2. using EventList = std::vector<epoll_event>;
  3. int epollfd_;
  4. EventList events_;
复制代码


  • kInitEventListSize :初始变乱列表巨细。
  • EventList :用于存储epoll变乱的向量范例。
  • int epollfd_ :epoll实例的文件形貌符。
  • EventList events_ :存储从epoll_wait返回的变乱列表。
成员函数

  1.     EPollPoller(EventLoop *Loop);
  2.     ~EPollPoller() override;
  3.     //重写基类Poller的抽象方法
  4.     Timestamp poll(int timeoutMs, ChannelList *activeChannels) override;
  5.     void updateChannel(Channel *channel) override;
  6.     void removeChannel(Channel *channel) override;
复制代码


  • Timestamp poll(int timeoutMs, ChannelList *activeChannels):

    • 调用epoll_wait期待变乱发生,将活跃的变乱填充到activeChannels中。

  • void updateChannel(Channel *channel):

    • 更新或添加一个Channel对象到epoll实例中,调用epoll_ctl。

  • void removeChannel(Channel *channel):

    • 从epoll实例中移除一个Channel对象,调用epoll_ctl。

具体实现

  1. #include "EpollPoller.h"
  2. #include "Logger.h"
  3. #include "Channel.h"
  4. #include <errno.h>
  5. #include <unistd.h>
  6. #include <strings.h>
  7. const int kNew = -1;
  8. const int kAdded = 1;
  9. const int kDeleted = -1;
  10. EPollPoller::EPollPoller(EventLoop *loop)
  11.     : Poller(loop)
  12.     , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
  13.     , events_(kInitEventListSize) { //创建了vector<epoll_events>
  14.         if (epollfd_ < 0) LOG_FATAL("epoll_create error:%d\n", errno);
  15. }
  16. EPollPoller::~EPollPoller() {
  17.     ::close(epollfd_);
  18. }
  19. Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
  20. {
  21.     LOG_INFO("func=%s => fd total count:%lu \n"
  22.         , __FUNCTION__
  23.         , channels_.size());
  24.    
  25.     int numEvents = ::epoll_wait(epollfd_
  26.                                  , &*events_.begin()
  27.                                  , static_cast<int>(events_.size())
  28.                                  , timeoutMs);
  29.     int saveErrno = errno;
  30.     Timestamp now(Timestamp::now());
  31.     if (numEvents > 0) {
  32.         LOG_INFO("%d events happened \n", numEvents);
  33.         fillActiveChannels(numEvents, activeChannels);
  34.         if (numEvents == events_.size()) {
  35.             events_.resize(events_.size() * 2);
  36.         }
  37.     } else if (numEvents == 0) {
  38.         LOG_DEBUG("%s timeout! \n", __FUNCTION__);
  39.     } else {
  40.         if (saveErrno != EINTR) {
  41.             errno = saveErrno;
  42.             LOG_ERROR("EPollPoller::poll() err!");
  43.         }
  44.     }
  45.     return Timestamp();
  46. }
  47. void EPollPoller::updateChannel(Channel *channel) {
  48.     const int index = channel->index();
  49.     // LOG_INFO("func=%s =>fd=%d events=%d index=%d\n"
  50.     //     , __FUNCTION__
  51.     //     , channel->fd
  52.     //     , channel->events()
  53.     //     , index)
  54.     if (index == kNew || index == kDeleted) {
  55.         if (index == kNew) {
  56.             int fd = channel->fd();
  57.             channels_[fd] = channel;
  58.         }
  59.         channel->set_index(kAdded);
  60.         update(EPOLL_CTL_ADD, channel);
  61.     } else { //说明channel已经在Poller注册过了
  62.         int fd = channel->fd();
  63.         if (channel->isNoneEvent()) {
  64.             update(EPOLL_CTL_DEL, channel);
  65.             channel->set_index(kDeleted);
  66.         } else {
  67.             update(EPOLL_CTL_MOD, channel);
  68.         }
  69.     }
  70. }
  71. //从poller中删除channel
  72. void EPollPoller::removeChannel(Channel *channel) {
  73.     int fd = channel->fd();
  74.     channels_.erase(fd);
  75.     LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);
  76.     int index = channel->index();
  77.     if (index == kAdded) {
  78.         update(EPOLL_CTL_DEL, channel);
  79.     }
  80.     channel->set_index(kNew);
  81. }
  82. //填写活跃的连接
  83. void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const {
  84.     for (int i = 0; i < numEvents; ++i) {
  85.         Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
  86.         channel->set_revents(events_[i].events);
  87.         activeChannels->push_back(channel); //EventLoop就拿到了它的poller给它返回的所有发生事件的channel列表了
  88.     }
  89. }
  90. //更新channel通道 epoll_ctl add/mod/del
  91. void EPollPoller::update(int operation, Channel *channel) {
  92.     epoll_event event;
  93.     bzero(&event, sizeof event);
  94.     int fd = channel->fd();
  95.     event.events = channel->events();
  96.     event.data.fd = fd;
  97.     event.data.ptr = channel;
  98.     if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) {
  99.         if (operation == EPOLL_CTL_DEL) {
  100.             LOG_ERROR("epoll_ctl del error:%d\n", errno);
  101.         } else {
  102.             LOG_FATAL("epoll_ctl add/mod error:%d\n", errno);
  103.         }
  104.     }
  105. }
复制代码
常量的作用

  1. // channel未添加到poller中
  2. const int kNew = -1;  // channel的成员index_ = -1
  3. // channel已添加到poller中
  4. const int kAdded = 1;
  5. // channel从poller中删除
  6. const int kDeleted = 2;
复制代码
他们主要用于表现 channel的状态,在后续的方法具体实现中会表现到。
构造函数和析构函数

  1. EPollPoller::EPollPoller(EventLoop *loop)
  2.     : Poller(loop)
  3.     , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
  4.     , events_(kInitEventListSize)  // vector<epoll_event>
  5. {
  6.     if (epollfd_ < 0)
  7.     {
  8.         LOG_FATAL("epoll_create error:%d \n", errno);
  9.     }
  10. }
  11. EPollPoller::~EPollPoller()
  12. {
  13.     ::close(epollfd_);
  14. }
复制代码


  • 构造函数:创建一个epoll实例epollfd_,随后我们需要初始化我们所关注的变乱列表巨细events_,
  • 析构函数:我们知道,我们将全部监控的变乱都委托给了内核的epoll实例来进行管理,该实例底层是一颗红黑树。我们最后析构的时间,可以直接关闭close,就可以关闭全部网络IO的文件形貌符了。
⭐️poll函数

  1. Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
  2. {
  3.     LOG_INFO("func=%s => fd total count:%lu \n"
  4.         , __FUNCTION__
  5.         , channels_.size());
  6.    
  7.     int numEvents = ::epoll_wait(epollfd_
  8.                                  , &*events_.begin()
  9.                                  , static_cast<int>(events_.size())
  10.                                  , timeoutMs);
  11.     int saveErrno = errno;
  12.     Timestamp now(Timestamp::now());
  13.     if (numEvents > 0) {
  14.         LOG_INFO("%d events happened \n", numEvents);
  15.         fillActiveChannels(numEvents, activeChannels);
  16.         if (numEvents == events_.size()) {
  17.             events_.resize(events_.size() * 2);
  18.         }
  19.     } else if (numEvents == 0) {
  20.         LOG_DEBUG("%s timeout! \n", __FUNCTION__);
  21.     } else {
  22.         if (saveErrno != EINTR) {
  23.             errno = saveErrno;
  24.             LOG_ERROR("EPollPoller::poll() err!");
  25.         }
  26.     }
  27.     return Timestamp();
  28. }
复制代码
他就是实现我们多路分发的函数:


  • poll 函数使用 epoll_wait 期待变乱发生,并将活跃的变乱填充到 activeChannels 中。
  • 假如发生变乱,将这些变乱填充到 activeChannels,并在须要时扩展变乱列表。
  • 返回当前的时间戳,主要是为了后续方便打日志和进行管理。
updateChannel函数

  1. void EPollPoller::updateChannel(Channel *channel)
  2. {
  3.     const int index = channel->index();
  4.     LOG_INFO("func=%s => fd=%d events=%d index=%d \n", __FUNCTION__, channel->fd(), channel->events(), index);
  5.     if (index == kNew || index == kDeleted)
  6.     {
  7.         int fd = channel->fd();
  8.         if (index == kNew)
  9.         {
  10.             channels_[fd] = channel;
  11.         }
  12.         channel->set_index(kAdded);
  13.         update(EPOLL_CTL_ADD, channel);
  14.     }
  15.     else
  16.     {
  17.         int fd = channel->fd();
  18.         if (channel->isNoneEvent())
  19.         {
  20.             update(EPOLL_CTL_DEL, channel);
  21.             channel->set_index(kDeleted);
  22.         }
  23.         else
  24.         {
  25.             update(EPOLL_CTL_MOD, channel);
  26.         }
  27.     }
  28. }
复制代码


  • updateChannel 函数根据 Channel 的当前状态(新添加或已删除)来决定是否添加或更新 epoll 实例中的变乱,该函数肯定会被EventLoop封装,然后再由Channel自己来进行调用。
  • 假如是新添加的 Channel,则在 epoll 中注册该文件形貌符。
  • 假如 Channel 没有感兴趣的变乱,则将其从 epoll 中删除。
removeChannel 函数

  1. void EPollPoller::removeChannel(Channel *channel)
  2. {
  3.     int fd = channel->fd();
  4.     LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);
  5.    
  6.     int index = channel->index();
  7.     if (index == kAdded)
  8.     {
  9.         update(EPOLL_CTL_DEL, channel);
  10.     }
  11.     channel->set_index(kNew);
  12. }
复制代码


  • removeChannel 函数将 Channel 从 epoll 实例中删除,并更新其状态。这一看就是我们的EventLoop需要调用的函数。
removeChannel 和updateChannel

从这两个函数理我们可以看出,他们其实是为EventLoop提供操纵Channel的方法。从代码的具体实现细节来看,我们可以明确到 channel 为什么要设置一个 index_ 标记,主要就是为了实现channel的复用,我们总不能每次有新连接都新建一个channel,连接断开就删除channel吧!
⭐️fillActiveChannels 函数

  1. void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
  2. {
  3.     for (int i = 0; i < numEvents; ++i)
  4.     {
  5.         Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
  6.         channel->set_revents(events_[i].events);
  7.         activeChannels->push_back(channel);
  8.     }
  9. }
复制代码


  • fillActiveChannels 函数将 epoll_wait 返回的全部活跃变乱填充到 activeChannels 列表中。
  • 然手我们介绍一下 event.data,我们将已经被激活的event直接拿到手,这里就需要用到我们的event.data.ptr:
    data字段是一个团结体,具体结构包罗了我们常用的int fd和void *ptr;
    ptr 是一个通用指针,可以用来指向任何范例的数据。它通常用于关联用户自定义的数据结构(这里是我们的Channel*),以便在变乱触发时可以快速访问这些数据。比方,你可以将 ptr 设置为你的应用程序中某个特定对象的指针,当对应的文件形貌符触发变乱时,你的应用程序可以通过 ptr 直接访问到这个对象
  • 然后调用channel的set_revents方法,可以将已经被激活的变乱直接初始化到我们的channel中。
  • 随后把 channel 推到我们的 activeChannels
⭐️update 函数

  1. void EPollPoller::update(int operation, Channel *channel)
  2. {
  3.     epoll_event event;
  4.     bzero(&event, sizeof event);
  5.    
  6.     int fd = channel->fd();
  7.     event.events = channel->events();
  8.     event.data.fd = fd;
  9.     event.data.ptr = channel;
  10.    
  11.     if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
  12.     {
  13.         if (operation == EPOLL_CTL_DEL)
  14.         {
  15.             LOG_ERROR("epoll_ctl del error:%d\n", errno);
  16.         }
  17.         else
  18.         {
  19.             LOG_FATAL("epoll_ctl add/mod error:%d\n", errno);
  20.         }
  21.     }
  22. }
复制代码
update 函数根据操纵范例(添加、修改或删除)调用 epoll_ctl 来更新 epoll 实例中的 Channel。
其实说白了update就是用来封装epoll_ctl的。
该函数被 EPollPoller::removeChannel、EPollPoller::updateChannel调用,用来更新Channel的封装的fd以及其需要监控的相关变乱。
总结

EPollPoller 类实现了基于 epoll 的 I/O 多路复用,通过监控多个文件形貌符上的变乱,并在变乱发生时通知相应的 Channel 对象来处理变乱。通过实现这些函数,EPollPoller 能够高效地管理和分发变乱。
下一节,我们将讲解EventLoop类的具体实现!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表