网站建设如何实现检索功能,网站策划内容有哪些,完全免费建站系统,h5个人博客网站模板libevent源码学习3—事件event
libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括:
文件描述符已经就绪, 可以读取或者写入文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)超时事件发生某信号用户触发事件 所有事件具有相似的生命周期。…libevent源码学习3—事件event
libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括:
文件描述符已经就绪, 可以读取或者写入文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)超时事件发生某信号用户触发事件 所有事件具有相似的生命周期。调用 libevent 函数设置事件并且关联到 event_base 之后, 事件进入“已初始化(initialized)”状态。此时可以将事件添加到 event_base 中这使之进入“未决(pending)” 状态。在未决状态下, 如果触发事件的条件发生(比如说文件描述符的状态改变, 或者超时时间到达 )则事件进入“激活(active)”状态(用户提供的)事件回调函数将被执行。如果配置为“持久的(persistent)”, 事件将保持为未决状态。否则, 执行完回调后, 事件不再是未决的。删除操作可以让未决事件成为非未决(已初始化)的 ; 添加操作可以让非未决事件再次成为未决的。
ps未决 简单来说就是一个已经产生的信号但是还没有传递给任何进程此时该信号的状态就称为未决状态。
1.创建事件
1.1 生成新事件
使用 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;
};1.2 事件标志
EV_TIMEOUT这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候, EV_TIMEOUT 标志是被忽略的: 可以在添加事件的时候设置超时, 也可以不设置。超时发生时,回调函数的 what 参数将带有这个标志。EV_READ表示指定的文件描述符已经就绪, 可以读取的时候, 事件将成为激活的。EV_WRITE表示指定的文件描述符已经就绪, 可以写入的时候, 事件将成为激活的。EV_SIGNAL用于实现信号检测EV_PERSIST表示事件是“持久的”EV_ET表示如果底层的 event_base 后端支持边沿触发事件默认水平则事件应该是边沿触发的。这个标志影响 EV_READ 和 EV_WRITE 的语义。
1.3 关于事件持久性
默认情况下每当未决事件成为激活的fd 已经准备好读取或者写入或者因为超时事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的可以在回调函数中再次对其调用 event_add()。
然而如果设置了 EV_PERSIST 标志事件就是持久的。这意味着即使其回调被激活事件还是会保持为未决状态。 如果想在回调中让事件成为非未决的可以对其调用 event_del()。
每次执行事件回调的时候持久事件的超时值会被复位。因此如果具有 EV_READ|EV_PERSIST 标志以及5秒(你设置的值)的超时值则事件将在以下情况下成为激活的:
套接字已经准备好被读取的时候从最后一次成为激活的开始已经过去5秒
1.4 信号事件
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)2.事件的未决和非未决
构造事件之后在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到 event_base。
2.1 设置未决事件
int event_add(struct event *ev, const struct timeval *tv);在非未决的事件上调用 event_add()将使其在配置的 event_base 中成为未决的。成功时函数返回0, 失败时返回-1。
如果 tv 为 NULL, 添加的事件不会超时。否则, tv 以秒和微秒指定超时值。
如果对已经未决的事件调用 event_add(), 事件将保持未决状态, 并在指定的超时时间被重新调度。
2.2 设置非未决事件
int event_del(struct event *ev);对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的, 调用将没有效果。成功时函数返回 0, 失败时返回-1。
注意: 如果在事件激活后, 其回调被执行前删除事件, 回调将不会执行。
3.事件的优先级
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;4.检查事件状态
有时候需要了解事件是否已经添加检查事件代表什么。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_SIGNALparam 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中查看。
5.一次触发事件
如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 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()相同。
6.手动激活事件
极少数情况下需要在事件的条件没有触发的时候让事件成为激活的。
/* 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 events callback.param ncalls an obsolete argument: this is ignored.
*/
void event_active(struct event *ev, int res, short ncalls);7.事件状态之间的转换 参考《libevent深入浅出》、libevent官方文档。