libevent源码分析:time-test例子
time-test例子是libevent自带的一个例子,通过libevent提供的定时事件来实现,间隔固定时间打印的功能。
1 /* 2 * gcc -g -o time-test time-test.c -levent_core 3 */ 4 5 #include <sys/types.h> 6 #include <event2/event-config.h> 7 #include <sys/stat.h> 8 #include <unistd.h> 9 #include <time.h> 10 #include <sys/time.h> 11 #include <fcntl.h> 12 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <errno.h> 17 18 #include <event2/event.h> 19 #include <event2/event_struct.h> 20 #include <event2/util.h> 21 22 struct timeval lasttime; 23 int event_is_persistent; 24 25 static void timeout_cb(evutil_socket_t fd, short event, void *arg) 26 { 27 struct timeval newtime, difference; 28 struct event *timeout = arg; 29 double elapsed; 30 31 evutil_gettimeofday(&newtime, NULL); 32 evutil_timersub(&newtime, &lasttime, &difference); 33 elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6); 34 35 printf("timeout_cb called at %d: %.3f seconds elapsed.\n", (int)newtime.tv_sec, elapsed); 36 lasttime = newtime; 37 38 if (!event_is_persistent) 39 { 40 struct timeval tv; 41 evutil_timerclear(&tv); 42 tv.tv_sec = 2; 43 event_add(timeout, &tv); 44 } 45 } 46 47 int main(int argc, char **argv) 48 { 49 struct event timeout; 50 struct timeval tv; 51 struct event_base *base; 52 int flags; 53 54 if (argc == 2 && strcmp(argv[1], "-p")) 55 { 56 event_is_persistent = 1; 57 flags = EV_PERSIST; 58 } 59 else 60 { 61 event_is_persistent = 0; 62 flags = 0; 63 } 64 65 /* Initalize the event library */ 66 base = event_base_new(); 67 68 /* Initalize one event */ 69 event_assign(&timeout, base, -1, flags, timeout_cb, (void*)&timeout); 70 71 evutil_timerclear(&tv); 72 tv.tv_sec = 20; 73 event_add(&timeout, &tv); 74 75 evutil_gettimeofday(&lasttime, NULL); 76 event_base_dispatch(base); 77 78 return 0; 79 }
这次就通过分析一个这个简单的例子来加深对libevent的理解。
1、首先通过event_base_new获取一个event_base对象。
2、通过event_assign来对一个event赋值(属于的event_base,监听的事件类型,回调函数等)。
3、通过event_add激活该event。
4、调用event_base_dispatch进入事件循环。
event_base_dispatch内部有一个很大的死循环,不停的调用io复用机制来监听指定文件描述符上的事件,并在相应事件发生的时候,触发相应的回调函数。
回调的堆栈如下:
可以看到是依次调用了event_base_loop()->event_process_active()->event_process_active_single_queue()->timeout_cb()。
1 int 2 event_base_loop(struct event_base *base, int flags) 3 { 4 const struct eventop *evsel = base->evsel; 5 struct timeval tv; 6 struct timeval *tv_p; 7 int res, done, retval = 0; 8 9 /* Grab the lock. We will release it inside evsel.dispatch, and again 10 * as we invoke user callbacks. */ 11 EVBASE_ACQUIRE_LOCK(base, th_base_lock); 12 13 if (base->running_loop) { 14 event_warnx("%s: reentrant invocation. Only one event_base_loop" 15 " can run on each event_base at once.", __func__); 16 EVBASE_RELEASE_LOCK(base, th_base_lock); 17 return -1; 18 } 19 20 base->running_loop = 1; 21 22 clear_time_cache(base); 23 24 if (base->sig.ev_signal_added && base->sig.ev_n_signals_added) 25 evsig_set_base_(base); 26 27 done = 0; 28 29 #ifndef EVENT__DISABLE_THREAD_SUPPORT 30 base->th_owner_id = EVTHREAD_GET_ID(); 31 #endif 32 33 base->event_gotterm = base->event_break = 0; 34 35 while (!done) { 36 base->event_continue = 0; 37 base->n_deferreds_queued = 0; 38 39 /* Terminate the loop if we have been asked to */ 40 if (base->event_gotterm) { 41 break; 42 } 43 44 if (base->event_break) { 45 break; 46 } 47 48 tv_p = &tv; 49 if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) { 50 timeout_next(base, &tv_p); 51 } else { 52 /* 53 * if we have active events, we just poll new events 54 * without waiting. 55 */ 56 evutil_timerclear(&tv); 57 } 58 59 /* If we have no events, we just exit */ 60 if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) && 61 !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) { 62 event_debug(("%s: no events registered.", __func__)); 63 retval = 1; 64 goto done; 65 } 66 67 event_queue_make_later_events_active(base); 68 69 clear_time_cache(base); 70 71 res = evsel->dispatch(base, tv_p); 72 73 if (res == -1) { 74 event_debug(("%s: dispatch returned unsuccessfully.", 75 __func__)); 76 retval = -1; 77 goto done; 78 } 79 80 update_time_cache(base); 81 82 timeout_process(base); 83 84 if (N_ACTIVE_CALLBACKS(base)) { 85 int n = event_process_active(base); 86 if ((flags & EVLOOP_ONCE) 87 && N_ACTIVE_CALLBACKS(base) == 0 88 && n != 0) 89 done = 1; 90 } else if (flags & EVLOOP_NONBLOCK) 91 done = 1; 92 } 93 event_debug(("%s: asked to terminate loop.", __func__)); 94 95 done: 96 clear_time_cache(base); 97 base->running_loop = 0; 98 99 EVBASE_RELEASE_LOCK(base, th_base_lock); 100 101 return (retval); 102 }
其实如果去打开event_base_loop()函数的实现来看,会发现对于事件的处理时分两步的,先是调用dispatch,把不同io复用机制得到的事件转换为统一的libevent事件,并将这些事件放到event_base的激活事件队列中,然后再调用event_process_active函数从激活事件队列中挨个读取每个事件并调用其回调函数。