就如libevent官网上所写的“libevent - an event notification library”,libevent就是一个基于事件通知机制的库,可以看出event是整个库的核心。event就是Reactor框架中的事件处理程序组件(event_handler),它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。
1.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结构体做了详细的解释(英文): View Code /** * @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() */
注意上述中提到的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_next、ev_active_next、ev_next_with_common_timeout、ev_io_next和ev_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:记录了当前激活事件的类型;
2 .libevent对event的管理
libevent对event的管理如下图所示:
event management
每次当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback() 函数执行事件处理,并根据就绪的句柄和事件类型填充cb_callback函数的参数。
3.事件属性设置接口函数
在libevent,有几个函数可以用于设置事件的属性:
1. event_set
用于设置event属性的event_set函数实际上是调用了event_assign。
```c
复制代码
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函数的定义为:
```c
1 int 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 }
其中,参数为:
另外,我们也可以在创建新事件的时候设定事件属性,具体函数是event_new。而event_new实际也是调用了event_assign来实现的,不同的是event_new需要先给事件分配空间:
复制代码
1 struct event *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 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 }
复制代码
4.事件相关的其他常用接口函数
1. event_new
创建事件(涉及内存分配)。
1. event_add
添加事件到event_base。
2. event_del
将事件从监听列表中移除。
3. event_free
释放由event_new创建的事件(内存)。从其定义可看出其与event_del的区别:
复制代码
1 void 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 *);
复制代码
5.关于超时和信号事件的特殊接口函数
为了方便对超时和信号事件的处理,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 /**@}*/