libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括:
libevent
函数设置事件并且关联到 event_base
之后, 事件进入“已初始化(initialized)”状态。此时可以将事件添加到 event_base
中,这使之进入“未决(pending)” 状态。在未决状态下, 如果触发事件的条件发生(比如说,文件描述符的状态改变, 或者超时时间到达 ),则事件进入“激活(active)”状态,(用户提供的)事件回调函数将被执行。如果配置为“持久的(persistent)”, 事件将保持为未决状态。否则, 执行完回调后, 事件不再是未决的。删除操作可以让未决事件成为非未决(已初始化)的 ; 添加操作可以让非未决事件再次成为未决的。ps:未决: 简单来说就是一个已经产生的信号,但是还没有传递给任何进程,此时该信号的状态就称为未决状态。
使用 event_new()
接口创建事件。
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
struct event *event_new(struct event_base *base, evutil_socket_t fd,
short events, event_callback_fn cb, void *arg);
void event_free(struct event *event);
event_new()
试图分配和构造一个用于 base
的新的事件。events
参数是上述标志的集合。如果 fd
非负, 则它是将被观察其读写事件的文件。事件被激活时, libevent
将调用 cb 函数, 传递这些参数: 文件描述符 fd, 表示所有被触发事件的位字段 , 以及构造事件时的 arg 参数。发生内部错误, 或者传入无效参数时, event_new()
将返回 NULL。
所有新创建的事件都处于已初始化和非未决状态 ,调用 event_add()可以使其成为未决的。
要释放事件, 调用 event_free()
。对未决或者激活状态的事件调用 event_free()
是安全的: 在释放事件之前, 函数将会使事件成为非激活和非未决的。
event_new()实现:
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events,
void (*cb)(evutil_socket_t, short, void *), void *arg)
{
struct event *ev;
ev = mm_malloc(sizeof(struct event));
if (ev == NULL)
return (NULL);
if (event_assign(ev, base, fd, events, cb, arg) < 0)
{
mm_free(ev);
return (NULL);
}
return (ev);
}
event_add()实现:
int event_add(struct event *ev, const struct timeval *tv)
{
int res;
if (EVUTIL_FAILURE_CHECK(!ev->ev_base))
{
event_warnx("%s: event has no event_base set.", __func__);
return -1;
}
EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
res = event_add_nolock_(ev, tv, 0);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
return (res);
}
event结构体定义:
struct event {
struct event_callback ev_evcallback;
/* for managing timeouts */
union {
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
} ev_timeout_pos;
evutil_socket_t ev_fd;
struct event_base *ev_base;
union {
/* used for io events */
struct {
LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/* used by signal events */
struct {
LIST_ENTRY (event) ev_signal_next;
short ev_ncalls;
/* Allows deletes in callback */
short *ev_pncalls;
} ev_signal;
} ev_;
short ev_events;
short ev_res; /* result passed to event callback */
struct timeval ev_timeout;
};
默认情况下,每当未决事件成为激活的(fd 已经准备好读取或者写入,或者因为超时),事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的,可以在回调函数中再次对其调用 event_add()。
然而,如果设置了 EV_PERSIST 标志,事件就是持久的。这意味着即使其回调被激活,事件还是会保持为未决状态。 如果想在回调中让事件成为非未决的,可以对其调用 event_del()。
每次执行事件回调的时候,持久事件的超时值会被复位。因此,如果具有 EV_READ|EV_PERSIST 标志,以及5秒(你设置的值)的超时值,则事件将在以下情况下成为激活的:
libevent 也可以监测 POSIX 风格的信号。要构造信号处理器
#define evsignal_new(b, x, cb, arg) \
event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
libevent 也提供了一组方便使用的宏用于处理信号事件:
#define evsignal_add(ev, tv) event_add((ev), (tv))
#define evsignal_assign(ev, b, x, cb, arg) \
event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg))
#define evsignal_del(ev) event_del(ev)
#define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv))
#define evsignal_initialized(ev) event_initialized(ev)
构造事件之后,在将其添加到 event_base
之前实际上是不能对其做任何操作的。使用event_add()
将事件添加到 event_base
。
int event_add(struct event *ev, const struct timeval *tv);
在非未决的事件上调用 event_add()将使其在配置的 event_base 中成为未决的。成功时函数返回0, 失败时返回-1。
如果 tv 为 NULL, 添加的事件不会超时。否则, tv 以秒和微秒指定超时值。
如果对已经未决的事件调用 event_add()
, 事件将保持未决状态, 并在指定的超时时间被重新调度。
int event_del(struct event *ev);
对已经初始化的事件调用 event_del()
将使其成为非未决和非激活的。如果事件不是未决的或者激活的, 调用将没有效果。成功时函数返回 0, 失败时返回-1。
注意: 如果在事件激活后, 其回调被执行前删除事件, 回调将不会执行。
int event_priority_set(struct event *ev, int pri)
{
event_debug_assert_is_setup_(ev);
if (ev->ev_flags & EVLIST_ACTIVE)
return (-1);
if (pri < 0 || pri >= ev->ev_base->nactivequeues)
return (-1);
ev->ev_pri = pri;
return (0);
}
注意优先级的值的范围在第7行源码。相关数据结构释义:
/* Active event management. */
/** An array of nactivequeues queues for active
event_callbacks (ones that have triggered, and whose callbacks need to be called).
Low priority numbers are more important, and stall higher ones.
*/
struct evcallback_list *activequeues;
/** The length of the activequeues array */
int nactivequeues;
有时候需要了解事件是否已经添加,检查事件代表什么。libevent中有很多函数来获取不同的信息。
/**
Checks if a specific event is pending or scheduled.
@param ev an event struct previously passed to event_add()
@param events the requested event type; any of EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL
@param tv if this field is not NULL, and the event has a timeout, this field is set to
hold the time at which the timeout will expire.
@return true if the event is pending on any of the events in 'what',
(that is to say, it has been added), or 0 if the event is not added.
*/
int event_pending(const struct event *ev, short events, struct timeval *tv);
event_pending()
函数确定给定的事件是否是未决的或者激活的。如果是,·而且 events 参数设置了 EV_READ、EV_WRITE、EV_SIGNAL 或者 EV_TIMEOUT 等标志,则函数会返回事件当前为之未决或者激活的所有标志 。
/**
Extract _all_ of arguments given to construct a given event. The event_base is copied into *base_out,
the fd is copied into *fd_out, and so on.
If any of the "_out" arguments is NULL, it will be ignored.
*/
void event_get_assignment(const struct event *event, struct event_base **base_out, evutil_socket_t *fd_out,
short *events_out, event_callback_fn *callback_out, void **arg_out);
event_get_assignment()
复制所有为事件分配的字段到提供的指针中。任何为 NULL 的参数会被忽略。
当然还有很多获取状态的函数,详情去源码event.c
中查看。
如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 event_base_once()
。
int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_fn, void *, const struct timeval *);
除了不支持 EV_SIGNAL 或者 EV_PERSIST 之外,这个函数的接口与 event_new()
相同。
极少数情况下,需要在事件的条件没有触发的时候让事件成为激活的。
/*
You can use this function on a pending or a non-pending event to make it active,
so that its callback will be run by event_base_dispatch() or event_base_loop().
One common use in multithreaded programs is to wake the thread running event_base_loop() from another thread.
@param ev an event to make active.
@param res a set of flags to pass to the event's callback.
@param ncalls an obsolete argument: this is ignored.
*/
void event_active(struct event *ev, int res, short ncalls);
参考《libevent深入浅出》、libevent官方文档。