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.
", (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 }
View Code

这次就通过分析一个这个简单的例子来加深对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()

其实如果去打开event_base_loop()函数的实现来看,会发现对于事件的处理时分两步的,先是调用dispatch,把不同io复用机制得到的事件转换为统一的libevent事件,并将这些事件放到event_base的激活事件队列中,然后再调用event_process_active函数从激活事件队列中挨个读取每个事件并调用其回调函数。

原文地址:https://www.cnblogs.com/lit10050528/p/5868370.html