Bootstrap

Nginx事件模块学习之epoll

epoll函数及调用

        1、调用epoll_create函数创建epoll对象:int epoll_create(int size)
                        返回一个句柄,之后epoll的使用都将依靠这个句柄来标识。
                        参数size告诉epoll所要处理的大致事件数目。

        2、调用epoll_ctl向epoll对象中添加、修改、删除所要监控连接的套接字:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
                        epfd是epoll描述符。
                        op参数为EPOLL_CTL_ADD(添加事件), EPOLL_CTL_MOD(修改事件), EPOLL_CTL_DEL(删除事件)三种。
                        fd参数是待检测的连接套接字。
                        event参数告诉epoll对怎样的事件感兴趣。

其中 epoll_event 结构体定义

                        struct epoll_event{
                             __uint32_t events;
                            epoll_data_t data;
                        }

                       events可以是以下几个宏的集合:
                            EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
                            EPOLLOUT:表示对应的文件描述符可以写;
                            EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
                            EPOLLERR:表示对应的文件描述符发生错误;
                            EPOLLHUP:表示对应的文件描述符被挂断;
                            EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
                            EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

        3、掉用epoll_wait从epoll对象中收集发生事件的连接:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
                        返回当前发送的事件个数。0:没有发送事件,-1:为出现错误。
                        epfd是epoll描述符。
                        events是由进程自身分配好的epoll_event结构体数组,内核负责将数据复制到该数组中。
                        maxevents是可以返回事件的最大个数,一般events数组的大小。
                        timeout为没有检测到事件发送最大等待时间。

epoll的Linux内核实现浅谈

        Linux内核的一些数据结构跟Nginx特别像尤其像链表、树结构。可以通过我的这篇博客:Nginx数据结构学习_向往天空的鱼-CSDN博客大概了解一下上述数据结构的实现原理。

        1、当某一进程调用epoll_create创建epoll对象时,Linux内核会为该epoll对象创建一个独立的eventpoll结构体,该结构体有两个与epoll调用密切相关的变量,如下:

        struct eventpoll{
                 ......
                struct rb_root rbr;    /* 红黑树的根节点,存储着该epoll对象所有监控的事件 */
                struct list_head rdllist;    /* 双向列表的头结点,存储epoll_wait返回给用户的事件 */
                ......
        }

        2、在epoll对象中,对应每一个监控的事件都会创建一个epitem结构体,包含每一个事件实时对应的信息,如下:

        struct epitem{
                ......
                struct rb_node rbn;    /* 红黑树的节点 */
                struct list_head rdllink;    /* 双向列表的结点 */
                struct epoll_filefd ffd;    /* 事件句柄等信息 */
                struct eventpoll *ep;    /* 指向其所属的eventpoll对象 */
                struct epoll_event event;    /* 期待的事件类型 */
                   ......
           }

上述两个结构体在epoll内部是如何安排布置的,如下图:

        当进程调用epoll_wait检查是否有发生事件的连接时,只需要检查对象中的rdllist双链表是否为空链表,如果不为空,则把这里的事件复制到用户态的内存中,同时将事件数量返回给进程。因此,epoll_wait的效率非常高。epoll_ctl在向epoll对象中添加、修改、删除事件时,从rbr红黑树中查找事件也非常快,也就是说,epoll是非常高效的,它可以轻易的处理百万级别的并发连接。

        epoll模型的代码实现及惊群效应的深入理解详解笔者另一篇博文:epoll模型实现及惊群效应_码农诗人的博客-CSDN博客,欢迎指正。

;