其它的一些接口evsignal_add(),evtimer_add,evbuffer_setcb都是在event_add的基础上进行封装。应用的流程原理本上面没什么区别。
我们随便看一个例子,然后按照应用流程来解析几个关键接口的实现。
int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int main(int argc, char** argv)
{
if( argc < 3 )
{
printf("please input 2 parameter\n");
return -1;
}
//两个参数依次是服务器端的IP地址、端口号
int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));
if( sockfd == -1)
{
perror("tcp_connect error ");
return -1;
}
printf("connect to server successful\n");
struct event_base* base = event_base_new();
struct event *ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, NULL);
event_add(ev_sockfd, NULL);
//监听终端输入事件
struct event* ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_msg_cb,(void*)&sockfd);
event_add(ev_cmd, NULL);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
event_base_new实现如下
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
我们调用event_base_new创建的event_base是利用event_config设置的。我们看看event_config的结构,然后分析event_config_new()返回的event_config都有些什么性质。
event_config实现如下
struct event_config {
TAILQ_HEAD(event_configq, event_config_entry) entries;//尾队列
int n_cpus_hint;
enum event_method_feature require_features;
enum event_base_config_flag flags;
};
event_config_new()实现如下
struct event_config *
event_config_new(void)
{
struct event_config *cfg = mm_calloc(1, sizeof(*cfg));
if (cfg == NULL)
return (NULL);
TAILQ_INIT(&cfg->entries);
return (cfg);
}
可以看到,event_config_new返回来的event_config,尾队列只有队列头没有entry,其它三个成员的值也都是0。基本没什么卵用。
event_base_new_with_config实现如下.基本上就是初始化event_base的数据成员。
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
#ifndef _EVENT_DISABLE_DEBUG_MODE
event_debug_mode_too_late = 1;
#endif
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
detect_monotonic();
gettime(base, &base->event_tv);
//创建最小堆
min_heap_ctor(&base->timeheap);
//普通事件队列创建
TAILQ_INIT(&base->eventqueue);
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
event_deferred_cb_queue_init(&base->defer_queue);
base->defer_queue.notify_fn = notify_base_cbq_callback;
base->defer_queue.notify_arg = base;
//调用event_config_new返回来的默认cfg的flags为0
if (cfg)
base->flags = cfg->flags;
evmap_io_initmap(&base->io);//io映射
evmap_signal_initmap(&base->sigmap);//signal映射。signal和io的映射看之前event_base的分析文章
event_changelist_init(&base->changelist);
base->evbase = NULL;
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
/*
eventops是一个全局数组,里面保存了后端各种multiplexer的封装。
各个平台支持的multiplexer方法libevent会预先封装好,然后在编译时检测平台支持的方法,然后生成对应的宏来设置eventops的值。
初始化的时候只需要从里面读出对应的handle然后赋值给event base中的成员函数base,
base就可以调用到multiplexer中的方法。
libevent强制每个multiplexer都要实现init,add,del,dispatch,dealloc五个接口。
*/
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/*
event_config_is_avoided_method会遍历cfg的尾队列nentries。
默认情况下,调用event_config_new返回来的默认cfg的nentries为空,
所以这个函数总是返回false
*/
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
base->evsel = eventops[i];
//初始化后端
base->evbase = base->evsel->init(base);
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);
return NULL;
}
if (evutil_getenv("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s", base->evsel->name);
/*
设置event_base的优先级,并初始化event_base中的就绪队列。
就绪队列的个数跟初始化时的优先级有关系。
默认情况下,event_base只有一个就绪活动队列。
*/
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
/* 多线程设置 */
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
if (EVTHREAD_LOCKING_ENABLED() &&
(!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
int r;
EVTHREAD_ALLOC_LOCK(base->th_base_lock,
EVTHREAD_LOCKTYPE_RECURSIVE);
base->defer_queue.lock = base->th_base_lock;
EVTHREAD_ALLOC_COND(base->current_event_cond);
r = evthread_make_base_notifiable(base);
if (r<0) {
event_warnx("%s: Unable to make base notifiable.", __func__);
event_base_free(base);
return NULL;
}
}
#endif
return (base);
}
event_new()的作用就是设置事件的类型、回调函数、回调函数参数、绑定的IO事件fd,并设置事件的初始状态为EVLIST_INIT,然后返回一个新创建的事件。
具体的实现代码如下
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);
}
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)
{
if (!base)
base = current_base;
_event_debug_assert_not_added(ev);
ev->ev_base = base;
ev->ev_callback = callback;//设置回调函数
ev->ev_arg = arg;//设置回调函数参数
ev->ev_fd = fd;
ev->ev_events = events;//设置事件类型
ev->ev_res = 0;
ev->ev_flags = EVLIST_INIT;//设置事件初始状态
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
if (events & EV_SIGNAL) {
if ((events & (EV_READ|EV_WRITE)) != 0) {
event_warnx("%s: EV_SIGNAL is not compatible with "
"EV_READ or EV_WRITE", __func__);
return -1;
}
ev->ev_closure = EV_CLOSURE_SIGNAL;
} else {
if (events & EV_PERSIST) {
evutil_timerclear(&ev->ev_io_timeout);
ev->ev_closure = EV_CLOSURE_PERSIST;
} else {
ev->ev_closure = EV_CLOSURE_NONE;
}
}
min_heap_elem_init(ev);
if (base != NULL) {
/* by default, we put new events into the middle priority */
ev->ev_pri = base->nactivequeues / 2;
}
_event_debug_note_setup(ev);
return 0;
}
先上源码。event_add主要是封装了event_add_internal。
event_add_internal的主要实现如下。略掉了一些检查代码。
static inline int
event_add_internal(struct event *ev, const struct timeval *tv,
int tv_is_absolute)
{
struct event_base *base = ev->ev_base;
int res = 0;
int notify = 0;
event_queue_insert
...
/*
IO事件和signal事件会执行这个if里面的代码。timeout事件不执行。
主要就是将信号值和IO的fd值映射到一个数组中。
需要映射的原因是libevent支持将一个fd或者信号值同时响应多个回调函数。具体请看以前章节的分析。
映射完毕后,调用event_queue_insert将事件插入到event_base的普通事件队列eventqueue中。并将其状态设置为EVLIST_INSERTED
*/
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
if (ev->ev_events & (EV_READ|EV_WRITE))
res = evmap_io_add(base, ev->ev_fd, ev);
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add(base, (int)ev->ev_fd, ev);
if (res != -1)
event_queue_insert(base, ev, EVLIST_INSERTED);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
res = 0;
}
}
//timeout事件执行这个if里面的代码。IO事件和signal事件不执行。
if (res != -1 && tv != NULL) {
struct timeval now;
int common_timeout;
if (ev->ev_closure == EV_CLOSURE_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv;
//如果事件已经在最小堆中,则将其从堆中删除
if (ev->ev_flags & EVLIST_TIMEOUT) {
/* XXX I believe this is needless. */
if (min_heap_elt_is_top(ev))
notify = 1;
event_queue_remove(base, ev, EVLIST_TIMEOUT);
}
/*如果这个timeout事件之前已经发生,然后又在自己的回调函数中调用event_add,那么将这个事件从就绪队列中删除*/
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
if (ev->ev_events & EV_SIGNAL) {
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
}
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
gettime(base, &now);
common_timeout = is_common_timeout(tv, base);
if (tv_is_absolute) {
ev->ev_timeout = *tv;
} else if (common_timeout) {
struct timeval tmp = *tv;
tmp.tv_usec &= MICROSECONDS_MASK;
evutil_timeradd(&now, &tmp, &ev->ev_timeout);
ev->ev_timeout.tv_usec |=
(tv->tv_usec & ~MICROSECONDS_MASK);
} else {
evutil_timeradd(&now, tv, &ev->ev_timeout);
}
event_debug((
"event_add: timeout in %d seconds, call %p",
(int)tv->tv_sec, ev->ev_callback));
//将事件加入最小堆
event_queue_insert(base, ev, EVLIST_TIMEOUT);
if (common_timeout) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
if (ev == TAILQ_FIRST(&ctl->events)) {
common_timeout_schedule(ctl, &now, ev);
}
} else {
//如果我们加入最小堆的事件处在堆的头部,则提早唤醒主线程
if (min_heap_elt_is_top(ev))
notify = 1;
}
}
//唤醒主线程
if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
}
Q1: timeout事件可以用min-head最小堆来存储,也可以用event_base中的类型为struct common_timeout_list 的common_timeout_queues数组来存储。那么什么时候该使用最小堆,什么时候使用common_timeout_queues队列呢?
A: 当一个timeout同时要响应上千个事件,同时调用上千个回调函数时,使用common_timeout_queues数组比最小堆高效得多。
Q2: 如何使用?
A: 创建timeout事件后,要调用event_base_init_common_timeout接口,然后再调用event_add
event_base_dispatch主要是包装了event_base_loop函数。几个关键的函数做了注释。
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
struct timeval tv;
struct timeval *tv_p;
int res, done, retval = 0;
/* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (base->running_loop) {
event_warnx("%s: reentrant invocation. Only one event_base_loop"
" can run on each event_base at once.", __func__);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return -1;
}
base->running_loop = 1;
clear_time_cache(base);
if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base(base);
done = 0;
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
base->th_owner_id = EVTHREAD_GET_ID();
#endif
base->event_gotterm = base->event_break = 0;
while (!done) {
base->event_continue = 0;
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
break;
}
if (base->event_break) {
break;
}
//检查系统事件是否被人为往前设置,如果是,则将最小堆和common_timeout_queues中的时间戳减去相应的值
timeout_correct(base, &tv);
tv_p = &tv;
if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv);
}
//event_base中没有普通等待事件和就绪事件,则直接结束循环
if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
}
/* update last old time */
gettime(base, &base->event_tv);
clear_time_cache(base);
res = evsel->dispatch(base, tv_p);
if (res == -1) {
event_debug(("%s: dispatch returned unsuccessfully.",
__func__));
retval = -1;
goto done;
}
update_time_cache(base);
//处理要就绪的timeout事件
timeout_process(base);
if (N_ACTIVE_CALLBACKS(base)) {
//处理就绪的IO事件和signal事件
int n = event_process_active(base);
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) == 0
&& n != 0)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
event_debug(("%s: asked to terminate loop.", __func__));
done:
clear_time_cache(base);
base->running_loop = 0;
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (retval);
}