• libevent之event


      就如libevent官网上所写的“libevent - an event notification library”,libevent就是一个基于事件通知机制的库,可以看出event是整个库的核心。event就是Reactor框架中的事件处理程序组件(event_handler),它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。

    event结构体

      event结构体定义在<event2/event_struct.h>中:

     1 struct event {
     2     TAILQ_ENTRY(event) ev_active_next;
     3     TAILQ_ENTRY(event) ev_next;
     4     /* for managing timeouts */
     5     union {
     6         TAILQ_ENTRY(event) ev_next_with_common_timeout;
     7         int min_heap_idx;
     8     } ev_timeout_pos;
     9     evutil_socket_t ev_fd;
    10 
    11     struct event_base *ev_base;
    12 
    13     union {                // I/O事件和信号事件不能同时设置
    14         /* used for io events */
    15         struct {
    16             TAILQ_ENTRY(event) ev_io_next;
    17             struct timeval ev_timeout;
    18         } ev_io;
    19 
    20         /* used by signal events */
    21         struct {
    22             TAILQ_ENTRY(event) ev_signal_next;
    23             short ev_ncalls;
    24             /* Allows deletes in callback */
    25             short *ev_pncalls;
    26         } ev_signal;
    27     } _ev;
    28 
    29     short ev_events;
    30     short ev_res;        /* result passed to event callback */
    31     short ev_flags;
    32     ev_uint8_t ev_pri;    /* smaller numbers are higher priority */
    33     ev_uint8_t ev_closure;
    34     struct timeval ev_timeout;
    35 
    36     /* allows us to adopt for different types of events */
    37     void (*ev_callback)(evutil_socket_t, short, void *arg);
    38     void *ev_arg;
    39 };

      在原文档中,作者对event结构体做了详细的解释(英文):

    /**
     * @struct event
     *
     * Structure to represent a single event.
     *
     * An event can have some underlying condition it represents: a socket
     * becoming readable or writeable (or both), or a signal becoming raised.
     * (An event that represents no underlying condition is still useful: you
     * can use one to implement a timer, or to communicate between threads.)
     *
     * Generally, you can create events with event_new(), then make them
     * pending with event_add().  As your event_base runs, it will run the
     * callbacks of an events whose conditions are triggered.  When you
     * longer want the event, free it with event_free().
     *
     * In more depth:
     *
     * An event may be "pending" (one whose condition we are watching),
     * "active" (one whose condition has triggered and whose callback is about
     * to run), neither, or both.  Events come into existence via
     * event_assign() or event_new(), and are then neither active nor pending.
     *
     * To make an event pending, pass it to event_add().  When doing so, you
     * can also set a timeout for the event.
     *
     * Events become active during an event_base_loop() call when either their
     * condition has triggered, or when their timeout has elapsed.  You can
     * also activate an event manually using event_active().  The even_base
     * loop will run the callbacks of active events; after it has done so, it
     * marks them as no longer active.
     *
     * You can make an event non-pending by passing it to event_del().  This
     * also makes the event non-active.
     *
     * Events can be "persistent" or "non-persistent".  A non-persistent event
     * becomes non-pending as soon as it is triggered: thus, it only runs at
     * most once per call to event_add().  A persistent event remains pending
     * even when it becomes active: you'll need to event_del() it manually in
     * order to make it non-pending.  When a persistent event with a timeout
     * becomes active, its timeout is reset: this means you can use persistent
     * events to implement periodic timeouts.
     *
     * This should be treated as an opaque structure; you should never read or
     * write any of its fields directly.  For backward compatibility with old
     * code, it is defined in the event2/event_struct.h header; including this
     * header may make your code incompatible with other versions of Libevent.
     *
     * @see event_new(), event_free(), event_assign(), event_get_assignment(),
     *    event_add(), event_del(), event_active(), event_pending(),
     *    event_get_fd(), event_get_base(), event_get_events(),
     *    event_get_callback(), event_get_callback_arg(),
     *    event_priority_set()
     */
    View Code

      注意上述中提到的pending和active的区别。pending表示的是监听事件的列表,而active表示的是已激活事件的列表。

      下面简单解释一下结构体中重要字段的含义:
      1)ev_events:说明要监听的事件类型(event支持I/O、超时和信号3种事件类型,),它的值可由以下字段位与而成:

     1 /**
     2  * @name event flags
     3  *
     4  * Flags to pass to event_new(), event_assign(), event_pending(), and
     5  * anything else with an argument of the form "short events"
     6  */
     7 /**@{*/
     8 /** Indicates that a timeout has occurred.  It's not necessary to pass
     9  * this flag to event_for new()/event_assign() to get a timeout. */
    10 #define EV_TIMEOUT    0x01
    11 /** Wait for a socket or FD to become readable */
    12 #define EV_READ       0x02
    13 /** Wait for a socket or FD to become writeable */
    14 #define EV_WRITE      0x04
    15 /** Wait for a POSIX signal to be raised*/
    16 #define EV_SIGNAL     0x08
    17 /**
    18  * Persistent event: won't get removed automatically when activated.
    19  *
    20  * When a persistent event with a timeout becomes activated, its timeout
    21  * is reset to 0.
    22  */
    23 #define EV_PERSIST    0x10
    24 /** Select edge-triggered behavior, if supported by the backend. */
    25 #define EV_ET         0x20
    26 /**@}*/

      2)ev_nextev_active_nextev_next_with_common_timeoutev_io_nextev_signal_next都是双向链表节点指针。它们是libevent对不同事件类型和在不同的时期,对事件的管理时使用到的字段。

      3)min_heap_idx或ev_next_with_common_timeout指明超时事件在小根堆中的索引或在timeout list中的位置。

      4)ev_base该事件所属的反应堆实例,这是一个event_base结构体。

      5)ev_fd,对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号。

      6)eb_flags:libevent用于标记event信息的字段,表明其当前的状态,可能的值有:

    1 #define EVLIST_TIMEOUT  0x01  // event在time堆中
    2 #define EVLIST_INSERTED 0x02  // event在已注册事件链表中
    3 #define EVLIST_SIGNAL   0x04  // 未见使用
    4 #define EVLIST_ACTIVE   0x08  // event在激活链表中
    5 #define EVLIST_INTERNAL 0x10  // 内部使用标记
    6 #define EVLIST_INIT     0x80  // event已被初始化

      7)ev_callback,event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,原型为:

    void (*ev_callback)(int fd, short events, void *arg)

      其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg;
      8)ev_arg:void*,表明可以是任意类型的数据,在设置event时指定;

      9)ev_ncalls:事件就绪执行时,调用ev_callback的次数,通常为1;

      10)ev_pncalls:指针,通常指向ev_ncalls或者为NULL;

      11)ev_res:记录了当前激活事件的类型;

    libevent对event的管理

      libevent对event的管理如下图所示:

      event management

      每次当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback() 函数执行事件处理,并根据就绪的句柄和事件类型填充cb_callback函数的参数。

    事件属性设置接口函数

      在libevent,有几个函数可以用于设置事件的属性:

      1. event_set

      用于设置event属性的event_set函数实际上是调用了event_assign。

    1 void
    2 event_set(struct event *ev, evutil_socket_t fd, short events,
    3       void (*callback)(evutil_socket_t, short, void *), void *arg)
    4 {
    5     int r;
    6     r = event_assign(ev, current_base, fd, events, callback, arg);
    7     EVUTIL_ASSERT(r == 0);
    8 }

      而event_assign函数的定义为:

     1 int
     2 event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
     3 {
     4     if (!base)
     5         base = current_base;
     6 
     7     _event_debug_assert_not_added(ev);
     8 
     9     ev->ev_base = base;
    10 
    11     ev->ev_callback = callback;
    12     ev->ev_arg = arg;
    13     ev->ev_fd = fd;
    14     ev->ev_events = events;
    15     ev->ev_res = 0;
    16     ev->ev_flags = EVLIST_INIT;
    17     ev->ev_ncalls = 0;
    18     ev->ev_pncalls = NULL;
    19 
    20     if (events & EV_SIGNAL) {
    21         if ((events & (EV_READ|EV_WRITE)) != 0) {
    22             event_warnx("%s: EV_SIGNAL is not compatible with "
    23                 "EV_READ or EV_WRITE", __func__);
    24             return -1;
    25         }
    26         ev->ev_closure = EV_CLOSURE_SIGNAL;
    27     } else {
    28         if (events & EV_PERSIST) {
    29             evutil_timerclear(&ev->ev_io_timeout);
    30             ev->ev_closure = EV_CLOSURE_PERSIST;
    31         } else {
    32             ev->ev_closure = EV_CLOSURE_NONE;
    33         }
    34     }
    35 
    36     min_heap_elem_init(ev);
    37 
    38     if (base != NULL) {
    39         /* by default, we put new events into the middle priority */
    40         ev->ev_pri = base->nactivequeues / 2;
    41     }
    42 
    43     _event_debug_note_setup(ev);
    44 
    45     return 0;
    46 }
    View Code

      其中,参数为:

      

      另外,我们也可以在创建新事件的时候设定事件属性,具体函数是event_new。而event_new实际也是调用了event_assign来实现的,不同的是event_new需要先给事件分配空间:

     1 struct event *
     2 event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
     3 {
     4     struct event *ev;
     5     ev = mm_malloc(sizeof(struct event));
     6     if (ev == NULL)
     7         return (NULL);
     8     if (event_assign(ev, base, fd, events, cb, arg) < 0) {
     9         mm_free(ev);
    10         return (NULL);
    11     }
    12 
    13     return (ev);
    14 }

      2. event_base_set

      在默认情况下,事件event会被注册到一个全局event_base指针current_base。使用该函数可以指定不同的event_base。如果一个进程中存在多个libevent实例,必须要调用该函数为event设置不同的event_base。

      该函数的定义如下:

     1 int
     2 event_base_set(struct event_base *base, struct event *ev)
     3 {
     4     /* Only innocent events may be assigned to a different base */
     5     if (ev->ev_flags != EVLIST_INIT)
     6         return (-1);
     7 
     8     _event_debug_assert_is_setup(ev);
     9 
    10     ev->ev_base = base;
    11     ev->ev_pri = base->nactivequeues/2;
    12 
    13     return (0);
    14 }

      3. event_priority_set

      在默认情况下,所有的event的优先级都被设定为 active event list 长度的一半(nactivequeues / 2)。该函数可用于设定event的优先级。优先级的数值越小,表示优先级越高。另外,函数event_base_priority_init可用于设定优先级的最大值。

      event_priority_set的函数定义如下:

     1 /*
     2  * Set's the priority of an event - if an event is already scheduled
     3  * changing the priority is going to fail.
     4  */
     5 
     6 int
     7 event_priority_set(struct event *ev, int pri)
     8 {
     9     _event_debug_assert_is_setup(ev);
    10 
    11     if (ev->ev_flags & EVLIST_ACTIVE)
    12         return (-1);
    13     if (pri < 0 || pri >= ev->ev_base->nactivequeues)
    14         return (-1);
    15 
    16     ev->ev_pri = pri;
    17 
    18     return (0);
    19 }

    事件相关的其他常用接口函数

      1. event_new

      创建事件(涉及内存分配)。

      1. event_add

      添加事件到event_base。

      2. event_del

      将事件从监听列表中移除。

      3. event_free

      释放由event_new创建的事件(内存)。从其定义可看出其与event_del的区别:

     1 void
     2 event_free(struct event *ev)
     3 {
     4     _event_debug_assert_is_setup(ev);
     5 
     6     /* make sure that this event won't be coming back to haunt us. */
     7     event_del(ev);
     8     _event_debug_note_teardown(ev);
     9     mm_free(ev);
    10 
    11 }

      3. event_callback_fn

      事件的回调函数,用于执行具体的I/O操作。其定义如下:

     1 /**
     2    A callback function for an event.
     3 
     4    It receives three arguments:
     5 
     6    @param fd An fd or signal
     7    @param events One or more EV_* flags
     8    @param arg A user-supplied argument.
     9 
    10    @see event_new()
    11  */
    12 typedef void (*event_callback_fn)(evutil_socket_t, short, void *);

    关于超时和信号事件的特殊接口函数

      为了方便对超时和信号事件的处理,libevent特别为它们定义了接口函数(实际是对通用函数的封装)。

      超时事件:

     1 /**
     2    @name evtimer_* macros
     3 
     4     Aliases for working with one-shot timer events */
     5 /**@{*/
     6 #define evtimer_assign(ev, b, cb, arg) 
     7     event_assign((ev), (b), -1, 0, (cb), (arg))
     8 #define evtimer_new(b, cb, arg)           event_new((b), -1, 0, (cb), (arg))
     9 #define evtimer_add(ev, tv)               event_add((ev), (tv))
    10 #define evtimer_del(ev)                   event_del(ev)
    11 #define evtimer_pending(ev, tv)           event_pending((ev), EV_TIMEOUT, (tv))
    12 #define evtimer_initialized(ev)           event_initialized(ev)
    13 /**@}*/

      信号事件:

     1 /**
     2    @name evsignal_* macros
     3 
     4    Aliases for working with signal events
     5  */
     6 /**@{*/
     7 #define evsignal_add(ev, tv)        event_add((ev), (tv))
     8 #define evsignal_assign(ev, b, x, cb, arg)            
     9     event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg))
    10 #define evsignal_new(b, x, cb, arg)                
    11     event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
    12 #define evsignal_del(ev)            event_del(ev)
    13 #define evsignal_pending(ev, tv)    event_pending((ev), EV_SIGNAL, (tv))
    14 #define evsignal_initialized(ev)    event_initialized(ev)
    15 /**@}*/

    参考资料

      libevent源码深度剖析五 

  • 相关阅读:
    [LeetCode] 34. 在排序数组中查找元素的第一个和最后一个位置
    [LeetCode] 32. 最长有效括号
    [LeetCode] 31. 下一个排列
    [LeetCode] 30. 串联所有单词的子串
    [LeetCode] 29. 两数相除
    [LeetCode] 27. 移除元素
    转:畅享云时代:开发者必备的8个最佳云端集成开发环境
    转:前端集锦:十款精心挑选的在线 CSS3 代码生成工具
    转:So Easy!让开发人员更轻松的工具和资源
    转:Backbone与Angular的比较
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4820838.html
走看看 - 开发者的网上家园