zoukankan      html  css  js  c++  java
  • libevent源码学习_event结构体

    在libevent中最重要的结构体莫过于event和event_base了,下面对于这2个结构体进行分析。

    1、结构体event,位于:event.h

     1 struct event {
     2     /*
     3      * 双向链表节点指针
     4      * 是libevent对不同事件类型和在不同的时期 对事件的管理时使用到的字段
     5      */
     6     /*增加下一个事件*/ 
     7     TAILQ_ENTRY (event) ev_next;
     8     /*增加下一个活动事件*/
     9     TAILQ_ENTRY (event) ev_active_next;
    10     /*增加下一个信号*/
    11     TAILQ_ENTRY (event) ev_signal_next;
    12 
    13     /*
    14      * timeout事件 表示该event保存在min_heap数组中的索引
    15      */
    16     /* for managing timeouts */ 
    17     unsigned int min_heap_idx;    
    18 
    19     /*
    20      * 该事件所属的反应堆实例
    21      * 一个全局变量,管理着事件,如事件总数,事件列表
    22      */
    23     struct event_base *ev_base;
    24     
    25     /*
    26      * 对于I/O事件,是绑定的文件描述符
    27      * 对于signal事件,是绑定的信号
    28      */
    29     int ev_fd;
    30     
    31     /*
    32      * event关注的事件类型,它可以是以下3种类型:
    33      * I/O事件:EV_WRITE和EV_READ
    34      * 定时事件:EV_TIMEOUT
    35      * 信号:EV_SIGNAL
    36      * 辅助选项:EV_PERSIST,表明是一个永久事件
    37      */
    38     short ev_events;
    39     
    40     /*
    41      * 事件就绪执行时,调用ev_callback的次数,通常为1
    42      */
    43     short ev_ncalls;
    44 
    45     /*
    46      * 指针,通常指向ev_ncalls或者为NULL
    47      */
    48     /* Allows deletes in callback */
    49     short *ev_pncalls;    
    50 
    51     /*
    52      * timeout事件 超时值
    53      */
    54     struct timeval ev_timeout;
    55 
    56     /*
    57      * 优先级,越小越高
    58      */
    59     /* smaller numbers are higher priority */ 
    60     int ev_pri;        
    61 
    62     /*
    63      * event的回调函数,被ev_base调用,执行事件处理程序
    64      * 各参数说明如下:
    65      * 参数1: ev_fd,表示触发事件的文件句柄,比如一个socket对象
    66      * 参数2: ev_events,表示事件发生的结果,可能是超时、可读、可写
    67      * 参数3: ev_arg,表示传递给事件的参数
    68      */
    69     void (*ev_callback)(int, short, void *arg);
    70 
    71     /*
    72      * 回调函数的参数
    73      */
    74     void *ev_arg;
    75 
    76     /*
    77      * 记录了当前激活事件的类型
    78      */
    79     /* result passed to event callback */ 
    80     int ev_res;        
    81 
    82     /*
    83      * libevent用于标记event信息的字段,表明其当前的状态
    84      */
    85     int ev_flags;
    86 };

    2、结构体event_base,位于:event_internal.h

     1 struct event_base {
     2     /*
     3      * 表示选择的事件引擎,可能为: epoll, poll, select
     4      */
     5     const struct eventop *evsel;
     6 
     7     /*
     8      * 全局对象,evbase实际上是一个eventop实例对象,执行具体任务
     9      * 在函数event_base_new()中被初始化base->evbase = base->evsel->init(base)
    10      */
    11     void *evbase;
    12 
    13     /* counts number of total events */
    14     int event_count;        
    15 
    16     /* counts number of active events */
    17     int event_count_active;    
    18 
    19     /* Set to terminate loop */
    20     int event_gotterm;        
    21 
    22     /* Set to terminate loop immediately */
    23     int event_break;        
    24 
    25     /*
    26      * libevent支持事件优先级,因此可以把它看作是数组
    27      * 其中的元素activequeues[priority]是一个链表
    28      * 链表的每个节点指向一个优先级为priority的就绪事件event
    29      */
    30     /* active event management */
    31     struct event_list **activequeues;
    32     
    33     int nactivequeues;
    34 
    35     /* 
    36      * 用来管理信号的结构体
    37      */
    38     /* signal handling info */
    39     struct evsignal_info sig;
    40 
    41     /*
    42      * 链表,保存了所有的注册事件event的指针
    43      */
    44     struct event_list eventqueue;
    45 
    46     /*
    47      * 系统的当前时间
    48      */
    49     struct timeval event_tv;
    50 
    51     /*
    52      * 管理定时事件的小根堆
    53      */
    54     struct min_heap timeheap;
    55 
    56     /*
    57      * 与event::ev_timeout进行比较,确定事件是否超时
    58      */
    59     struct timeval tv_cache;
    60 };

    3、结构体eventop,位于:event_internal.h

     1 struct eventop {
     2     const char *name;
     3 
     4     /*
     5      * 初始化
     6      */
     7     void *(*init)(struct event_base *);
     8 
     9     /*
    10      * 注册事件
    11      */
    12     int (*add)(void *, struct event *);
    13 
    14     /*
    15      * 删除事件
    16      */
    17     int (*del)(void *, struct event *);
    18 
    19     /*
    20      * 事件分发
    21      */
    22     int (*dispatch)(struct event_base *, void *, struct timeval *);
    23 
    24     /*
    25      * 注销,释放资源
    26      */
    27     void (*dealloc)(struct event_base *, void *);
    28 
    29     /* set if we need to reinitialize the event base */
    30     int need_reinit;
    31 };

    实际真正使用这个结构体是在event.c中,根据系统支持的不同系统调用来选取一组上述的eventop操作,这是用C语言模拟多态的典型用法。

     1 /* In order of preference */
     2 static const struct eventop *eventops[] = {
     3 #ifdef HAVE_EVENT_PORTS
     4     &evportops,
     5 #endif
     6 #ifdef HAVE_WORKING_KQUEUE
     7     &kqops,
     8 #endif
     9 #ifdef HAVE_EPOLL
    10     &epollops,
    11 #endif
    12 #ifdef HAVE_DEVPOLL
    13     &devpollops,
    14 #endif
    15 #ifdef HAVE_POLL
    16     &pollops,
    17 #endif
    18 #ifdef HAVE_SELECT
    19     &selectops,
    20 #endif
    21 #ifdef WIN32
    22     &win32ops,
    23 #endif
    24     NULL
    25 };

    有了上面的定义,比如我们系统使用epoll相关的系统调用,则会有对应的一组函数,位于:epoll.c

    1 const struct eventop epollops = {
    2     "epoll",
    3     epoll_init,
    4     epoll_add,
    5     epoll_del,
    6     epoll_dispatch,
    7     epoll_dealloc,
    8     1 /* need reinit */
    9 };

    上面提到的这些结构体对应的UML图如下:

    从这个关系中我们可以很容易的看出他们在Reactor模型中各自代表了什么。

    先看来一下Reactor模式的UML图:

                   图1

                            图2

    在Reactor模式中,有5个关键的参与者:

    • 描述符(handle):由操作系统提供的资源,用于识别每一个事件,如Socket描述符、文件描述符、信号的值等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自内部,如信号、定时器事件。
    • 同步事件多路分离器(event demultiplexer):事件的到来是随机的、异步的,无法预知程序何时收到一个客户连接请求或收到一个信号。所以程序要循环等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术实现。在linux系统上一般是select、poll、epol_waitl等系统调用,用来等待一个或多个事件的发生。I/O框架库一般将各种I/O复用系统调用封装成统一的接口,称为事件多路分离器。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。
    • 事件处理器(event handler):I/O框架库提供的事件处理器通常是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般声明为虚函数,以支持用户拓展。
    • 具体的事件处理器(concrete event handler):是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。
    • Reactor 管理器(reactor):定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模 板函数来处理这个事件。

    通过上面2张Reactor的UML图及描述,可以知道在libevent里面结构体和Reactor的对应关系:

    • 描述符(handle):对于I/O事件就是socket,对于信号就是绑定的信号。
    • 同步事件多路分离器(event demultiplexer):对应epoll、select、poll、kqueue、evport、devpoll等不同的系统调用。
    • 事件处理器(event handler):对应结构体event,该结构体中的成员void (*ev_callback)(int, short, void *arg)就是回调函数的模板,也就是面向对象中的虚函数。
    • 具体的事件处理器(concrete event handler):对应的具体回调函数都是通过函数event_set()去设置的。
    • Reactor 管理器(reactor):对应结构体event_base,里面根据系统选择系统调用的一组函数进行注册,并dispatch分发。

                            图3

    本文参考自:

    http://blog.csdn.net/sparkliang/article/details/4957744 (图1)

    http://blog.csdn.net/u013074465/article/details/46276967 (图2及原始Reactor模型的描述)

    http://blog.csdn.net/chinabhlt/article/details/43452977(图3)

  • 相关阅读:
    PHP笔记
    HTML5储存
    KeyDown,KeyPress和KeyUp详解(转)
    Vue.js和angular.js区别
    java 解析json的问题
    在Eclipse中使用JUnit4进行单元测试
    Ibatis代码自动生成工具——Abator安装与应用实例(图解)
    IT人员----怎么把电脑窗口设置成淡绿色
    Java面试题之数据库三范式是什么
    Java面试题之jsp相关
  • 原文地址:https://www.cnblogs.com/abc-begin/p/7602411.html
Copyright © 2011-2022 走看看