Muduo中Epoller设计和细节|青训营笔记

195 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的的第7天

Muduo事件处理之Poller类的设计

简要介绍下关联的几个类

1.1 Channel类

Channel类其中对应了一个文件描述符号表示监听了哪一个对象,并且是包括 感兴趣的事件:events , epoll返回的目前发生的事件:revents,并且设置了关注的事件的回调函数

Channel一般是用作别的类的直接成员或者是间接的成员

1.2 EventLoop类

很著名的思想:one loop per thread, 每个线程对应的一个EventLoop.

在对事件处理的这个过程中承担着算是中间人的作用,EventLoop内涵了成员变量:Poller & Channel

并且POLLER & CHANNEL中都有所属的EVENTLOOP对象

1.3 事件总体的改变的趋势(事件的注册)

Channel update remove => EvnentLoop updateChannel removeChannel => Poller updateChannel removeChannel

  • 某个地方调用Channel的update
  • Channel所属的EventLoopchannel进行操作
  • EventLoop调用底层的IO复用类:epoll

其中Channel中的很多的状态信息:index、fd等用于辅助epoll对其的操作


2.1 epoll的使用

1、内核事件表

不同于select & poll, epoll的创建需要一个fd, 之后使用的是epoll内置的对应的函数进行操作

epoll_create(int size) size只是一个提示说需要多大的空间, 返回的就是文件描述符

2、epoll的操作函数

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

  • 第一个参数就是上面创建的epoll fd
  • 第二个参数就是选择 添加、修改、删除(内置的宏定义)
  • 关注哪一个文件描述符
  • 对应的事件

3、epoll中event结构内容

typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;
​
           struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
           };

4、epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

如果检测到数据了,就将对应的事件复制到第二个参数:事件表中,然后遍历执行对事件的处理就可以了

2.2 EPOLLER的设计细节

Screen Shot 2022-08-10 at 12.55.09 PM.png

poll() : 封装epoll_wait

//epoll_wait, 将发生的事件填入activeChannels
TimeStamp Epoller::poll(int timeoutMs, ChannelList* activeChannels)
{
    int numEvents = ::epoll_wait(epollfd_,
                                 &(*events_.begin()),
                                 static_cast<int>(events_.size()),
                                 timeoutMs);
    
    TimeStamp now(TimeStamp::now());
    if (numEvents > 0) {
        //TODO: log 
        printf("%d events happend\n", numEvents);
        //将发生的事件填充进activeChannel中去
        fillActiveChannels(numEvents, activeChannels);
​
        //将事件表进行扩容
        if (events_.size() == numEvents) {
            events_.resize(events_.size() * 2);
        }
    } else if (numEvents == 0) {
        //do nothing 
    } else {
        if (errno != EINTR) {
            printf("EPoller::poll() : epoll_wait error\n");
        }
    }
    return now;
}

直接向上面讲的一样,调用epoll_wait, 遍历活跃的事件,填充到activeChannel中去

epoll_ctl

总的流程:fd所在的Channel想要注册事件,通过Channel对应的owner Loop(也就是属于哪一个事件循环)去调用update, Channel通过成员变量Epoller进行实际上的调用

Screen Shot 2022-08-10 at 3.15.06 PM.png

Screen Shot 2022-08-10 at 3.15.15 PM.png

下面是实际上的对于epoll_ctl的使用, 创建event结构然后调用,注册事件

\