• 具体的多路分发器:EPollPoller


    具体的多路分发器:EPollPoller

    Poller只是多路分发器的抽象,提供统一的接口,EPoller才展示了多路分发器的具体实现。
    在这里插入图片描述
    override是C11的新语法,表示在派生类中,这些方法都是覆盖方法,必须由编译器保证在基类里面一定有这些函数的接口声明。
    epoll_event被装在一个vector容器中,可以动态的扩容。
    epollfd_成员是epoll_create创建内核事件表返回的文件描述符。
    重写EPollPoller.h:

    #pragma once
    
    #include "Poller.h"
    #include "Timestamp.h"
    
    #include 
    #include 
    
    /*
        epoll的使用:
        epoll_create
        epoll_ctl     add/remove/modify
        epoll_wait
    */
    
    class Channel;
    
    class EPollPoller : public Poller
    {
    public:
        EPollPoller(EventLoop* loop);
        ~EPollPoller() override;
    
        //重写基类的Poller抽象方法
        Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;
        void updateChannel(Channel* channel) override;
        void removeChannel(Channel* channel) override;
    private:
        static const int kInitEventListSize =16;
    
        //填写活跃的连接
        void fillActiveChannels(int numEvents, ChannelList* activeChannels) const;
        //更新channel通道
        void update(int operation, Channel* channel);
        
        using EventList = std::vector<epoll_event>;
    
        int epollfd_;
        EventList events_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    重写EPollPoller.cc:

    #include "EPollPoller.h"
    #include "Logger.h"
    #include "Channel.h"
    
    #include 
    #include 
    #include 
    
    //channel中的index_成员表示channel在poller的状态
    //channel未添加到poller中
    const int kNew = -1;
    //channel已添加到poller中
    const int kAdded = 1;
    //channel从poller中删除
    const int kDeleted = 2;
    
    EPollPoller::EPollPoller(EventLoop* loop)
        :Poller(loop)
        ,epollfd_(::epoll_create1(EPOLL_CLOEXEC))
        ,events_(kInitEventListSize) //vector
    {
        if(epollfd_ < 0)
        {
            LOG_FATAL("epoll_create error:%d \n", errno);
        }
    }
    
    EPollPoller::~EPollPoller()
    {
        ::close(epollfd_);
    }
    
    Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
    {
        //应该用LOG_DEBUG 输出日志更合理
        //LOG_INFO("func=%s => fd total count:%lu\n",__FUNCTION__ , channels_.size());
    
        int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);
        int saveErrno = errno;
        Timestamp now(Timestamp::now());
    
        if(numEvents > 0)
        {
            LOG_INFO("%d events happened \n", numEvents);
            fillActiveChannels(numEvents,activeChannels);
            if(numEvents == events_.size())
            {
                events_.resize(events_.size() * 2);    
            }
        }
        else if(numEvents == 0)
        {
            LOG_DEBUG("%s timeout! \n", __FUNCTION__)
        }
        else{
            if(saveErrno != EINTR)
            {
                errno = saveErrno;
                LOG_ERROR("EPollPoller::poll() err!");
            }
        }
        return now;
    }
    
    void EPollPoller::updateChannel(Channel* channel)
    {
        const int index = channel->index();
        int fd = channel->fd();
        //LOG_INFO("func=%s => fd=%d events=%d index=%d \n", __FUNCTION__, fd, channel->events(), index);
    
        if(kNew == index || kDeleted == index)
        {
            if(index == kNew)
            {
                channels_[fd] = channel;
            }
    
            channel->set_index(kAdded);
            update(EPOLL_CTL_ADD, channel);
        }
        else //已经在poller上注册过了
        {
            if(channel->isNoneEvent())
            {
                update(EPOLL_CTL_DEL, channel);
                channel->set_index(kDeleted);
            }
            else
            {
                update(EPOLL_CTL_MOD, channel);
            }
        }
    
    }
    
    //从poller中删除channel
    void EPollPoller::removeChannel(Channel* channel)
    {
        int fd = channel->fd();
        channels_.erase(fd);
    
        //LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);
    
        int index = channel->index();
        if(index == kAdded)
        {
            update(EPOLL_CTL_DEL,channel);
        }
    
        channel->set_index(kNew);
    }
    
    //填写活跃的连接
    void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const
    {
        for(int i = 0; i < numEvents; i++)
        {
            Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
            channel->set_revents(events_[i].events);
            activeChannels->push_back(channel);
            //EventLoop就拿到了它的poller给他返回的所有发生事件的channel列表了
        }
    }
    
    //更新channel通道
    void EPollPoller::update(int operation, Channel* channel)
    {
        epoll_event event;
        bzero(&event, sizeof event);
    
        int fd = channel->fd();
        event.events = channel->events();
        event.data.ptr = channel;
    
        if(::epoll_ctl(epollfd_, operation, fd, &event) < 0)
        {
            if(operation == EPOLL_CTL_DEL)
            {
                LOG_ERROR("epoll_ctl del error:%d\n",errno);
            }
            else
            {
                LOG_FATAL("epoll_ctl add/modify error:%d\n",errno);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146

    muduo在创建epoll实例是调用的是::epoll_create1(EPOLL_CLOEXEC),看epoll_create和epoll_create1的区别:
    在这里插入图片描述
    epoll_create的参数size自Linux2.6.8以后就没有意义了,但必须是一个大于0的数。
    而epoll_create1的参数flags如果是0,那和epoll_create没有区别,但调用epoll_create1可以给返回的文件描述符加一个EPOLL_CLOEXEC的标志。默认情况下fork产生的子进程是会共享所有父进程的资源的,如果加上这个标志,在子进程exec时就会把带这个标志的资源释放掉。

    在这里插入图片描述
    epoll_event结构中,除了关注的事件events,还有一个union epoll_data_t,这个联合体可以根据我们的需求传进一个自定义的void*,但这个变量必须包含我们关注事件的fd,不然事件发生时,我们根本获取不到发生事件的fd。muduo绑定的是channel*,这样就可以通过channel的channel->fd()获取到发生事件的fd了。
    在这里插入图片描述

  • 相关阅读:
    基于SSM学术会议管理系统毕业设计源码061504
    OpenGauss数据库在 CentOS 上的实践,配置篇
    【安装填坑】-import win32api, sys, os ImportError: DLL load failed: 找不到指定的模块。
    Java剑指Offer二维数组中的查找
    【C进阶】指针和数组笔试题解析
    文件重命名自动化:批量处理让生活更简单
    JMeter从数据库中获取数据并作为变量使用
    大学生HTML作业篮球网页 HTML作业篮球网页期末作业 HTML+CSS篮球网页 HTML学生作业体育篮球网页
    基于SSH的客户关系管理系统的设计与实现(JavaWeb开发的CRM管理系统)
    【Linux】初识系统调用&&进程状态
  • 原文地址:https://blog.csdn.net/weixin_43973403/article/details/126166348