libevent是事件驅動的網絡庫,事件驅動是他的核心,所以理解事件驅動對於理解整個網絡庫有很重要的意義。
本着從簡入繁,今天分析下單線程最簡單的事件觸發。通過sample下的event-test來理解libevent的事件驅動。
代碼版本為1.4.14。
libevent事件機制:當事件發生, libevent就會根據用戶設定的方式自動執行指定的回調函數,來處理事件。
這是一種reactor方式的事件通知方式,由事件驅動。reactor的優點:響應快,編程簡單等等。。。
首先看下幾個重要的結構。等全部分析完libevent,再把全部注釋過的代碼上傳到github上。如果有錯誤及時告訴我,謝謝。
1.event_base
我的理解是當前線程中所有事件的一個管理者。位於event-internal.h中。
1 //事件基礎管理 2 struct event_base { 3 //I/O復用類型,select、epoll...linux默認是epoll 4 const struct eventop *evsel; 5 //具體的I/O復用,是epollop類型,通過eventop中的init函數返回,包含了具體的I/O復用各種信息 6 void *evbase; 7 //總共的事件個數 8 int event_count; /* counts number of total events */ 9 //總共的活動事件個數 10 int event_count_active; /* counts number of active events */ 11 12 //退出 13 int event_gotterm; /* Set to terminate loop */ 14 //立即退出 15 int event_break; /* Set to terminate loop immediately */ 16 17 /* active event management */ 18 //活動事件隊列,二維鏈表。第一維是根據優先級,第二維是每個優先級中對應加入的事件 19 struct event_list **activequeues; 20 //優先級隊列數量。數組第一維必須告訴大小。因為如果是數組,參入函數,第一維肯定退化為指針,無法知道長度 21 int nactivequeues; 22 23 //信號信息 24 /* signal handling info */ 25 struct evsignal_info sig; 26 27 //所有事件隊列 28 struct event_list eventqueue; 29 30 //event_base創建時間 31 struct timeval event_tv; 32 33 //event_base時間小根堆 34 struct min_heap timeheap; 35 36 //event_base緩存時間 37 struct timeval tv_cache; 38 };
2.eventop
當前選用的I/O復用模型的封裝。位於event-internal.h中。
1 //I/O復用封裝 2 struct eventop { 3 const char *name; 4 void *(*init)(struct event_base *); //初始化 5 int (*add)(void *, struct event *); //注冊 6 int (*del)(void *, struct event *); //刪除 7 int (*dispatch)(struct event_base *, void *, struct timeval *); //事件分發 8 void (*dealloc)(struct event_base *, void *);//釋放資源 9 /* set if we need to reinitialize the event base */ 10 int need_reinit; 11 };
3.event
事件信息的封裝
1 struct event { 2 //事件在隊列中的節點(下次分析此隊列的實現) 3 TAILQ_ENTRY (event) ev_next; 4 TAILQ_ENTRY (event) ev_active_next; 5 TAILQ_ENTRY (event) ev_signal_next; 6 //事件在最小時間堆中位置 7 unsigned int min_heap_idx; /* for managing timeouts */ 8 9 //事件的當前管理類 10 struct event_base *ev_base; 11 //事件對應的文件描述符,一切皆文件 12 int ev_fd; 13 //事件類型 14 short ev_events; 15 //發送到活動隊列后要執行的次數 16 short ev_ncalls; 17 //ev_pncalls指向ev_ncalls,允許在回調中將自己的事件執行次數置為0,然后退出 18 short *ev_pncalls; /* Allows deletes in callback */ 19 20 //事件觸發的時間 21 struct timeval ev_timeout; 22 23 //事件優先級 24 int ev_pri; /* smaller numbers are higher priority */ 25 26 //事件到來回調 27 void (*ev_callback)(int, short, void *arg); 28 //事件到來回調的參數 29 void *ev_arg; 30 31 //事件在活動隊列中的事件類型,發送給回調函數,讓回調函數知道發生事件的原因 32 int ev_res; /* result passed to event callback */ 33 34 //標識該事件在哪個隊列中,插入的是哪個隊列 35 int ev_flags; 36 };
4.接着看幾個比較重要的宏定義
1 //隊列標記 2 //定時器隊列,與時間有關的事件加入此隊列 3 #define EVLIST_TIMEOUT 0x01 4 //總隊列,代表已經插入過 5 #define EVLIST_INSERTED 0x02 6 //信號隊列 7 #define EVLIST_SIGNAL 0x04 8 //活動隊列 9 #define EVLIST_ACTIVE 0x08 10 //內部隊列 11 #define EVLIST_INTERNAL 0x10 12 //初始化隊列 13 #define EVLIST_INIT 0x80 14 15 /* EVLIST_X_ Private space: 0x1000-0xf000 */ 16 #define EVLIST_ALL (0xf000 | 0x9f) 17 //事件類型,發生了什么事件 18 19 //定時超時,表明事件超時,如果在活動隊列中,需要執行 20 #define EV_TIMEOUT 0x01 21 //I/O事件 22 #define EV_READ 0x02 23 #define EV_WRITE 0x04 24 //信號 25 #define EV_SIGNAL 0x08 26 //持續事件 27 #define EV_PERSIST 0x10 /* Persistant event */
事件機制流程圖
通過流程圖可以更加清晰的理解。

測試代碼
test.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/queue.h> 4 #include <sys/time.h> 5 #include <fcntl.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <errno.h> 11 int main(int argc,char **argv){ 12 char *input = argv[1]; 13 if(argc !=2){ 14 input = "hello"; 15 } 16 int fd; 17 fd = open("event.fifo",O_WRONLY); 18 if(fd == -1){ 19 perror("open error"); 20 exit(EXIT_FAILURE); 21 } 22 write(fd,input,strlen(input)); 23 close(fd); 24 printf("write success\n"); 25 return 0; 26 }
1.首先運行./event-test

可以看到linux默認的I/O復用是epoll。加入隊列,並且不是定時函數。最后進入循環到達epoll_dispatch分發。
2.另一個終端中運行./test
此時上面一個終端可以收到如下信息:事件觸發,調用event_del,從相應隊列中刪除,然后執行,event-test.c中再次加入了事件,進入事件循環。

具體函數注釋:
1.event_base_new
1 //創建event_base 2 struct event_base * 3 event_base_new(void) 4 { 5 int i; 6 struct event_base *base; 7 //申請空間 8 if ((base = calloc(1, sizeof(struct event_base))) == NULL) 9 event_err(1, "%s: calloc", __func__); 10 11 event_sigcb = NULL; 12 event_gotsig = 0; 13 //是否使用絕對時間 14 detect_monotonic(); 15 //獲取event_base創建時間 16 gettime(base, &base->event_tv); 17 18 //初始化小根堆 19 min_heap_ctor(&base->timeheap); 20 //初始化隊列 21 TAILQ_INIT(&base->eventqueue); 22 //信號相關 23 base->sig.ev_signal_pair[0] = -1; 24 base->sig.ev_signal_pair[1] = -1; 25 26 base->evbase = NULL; 27 //獲得I/O復用,選到合適的就往下執行。linux默認是epoll 28 for (i = 0; eventops[i] && !base->evbase; i++) { 29 //獲得I/O復用 30 base->evsel = eventops[i]; 31 //獲得具體的I/O復用信息 32 base->evbase = base->evsel->init(base); 33 } 34 //沒有I/O復用,報錯退出 35 if (base->evbase == NULL) 36 event_errx(1, "%s: no event mechanism available", __func__); 37 //如果設置了EVENT_SHOW_METHOD,輸出IO復用名字 38 if (evutil_getenv("EVENT_SHOW_METHOD")) 39 event_msgx("libevent using: %s\n", 40 base->evsel->name); 41 42 /* allocate a single active event queue */ 43 //初始化活動隊列的優先級,默認優先級為1 44 event_base_priority_init(base, 1); 45 46 return (base); 47 }
2.event_base_priority_init
1 //初始化優先隊列 2 int 3 event_base_priority_init(struct event_base *base, int npriorities) 4 { 5 int i; 6 //如果base中有活動事件,返回,不處理優先級的初始化 7 if (base->event_count_active) 8 return (-1); 9 //如果優先級數量未變,沒有必要執行 10 if (npriorities == base->nactivequeues) 11 return (0); 12 //釋放所有優先級隊列 13 if (base->nactivequeues) { 14 for (i = 0; i < base->nactivequeues; ++i) { 15 free(base->activequeues[i]); 16 } 17 free(base->activequeues); 18 } 19 20 /* Allocate our priority queues */ 21 //分配優先級隊列 22 base->nactivequeues = npriorities; 23 base->activequeues = (struct event_list **) 24 calloc(base->nactivequeues, sizeof(struct event_list *)); 25 if (base->activequeues == NULL) 26 event_err(1, "%s: calloc", __func__); 27 //默認每個優先級分配一個節點,作為事件隊列的隊列的頭結點 28 for (i = 0; i < base->nactivequeues; ++i) { 29 base->activequeues[i] = malloc(sizeof(struct event_list)); 30 if (base->activequeues[i] == NULL) 31 event_err(1, "%s: malloc", __func__); 32 //每個事件都初始化為隊列的頭結點 33 TAILQ_INIT(base->activequeues[i]); 34 } 35 36 return (0); 37 }
3.event_set
1 //設置與注冊event 2 //ev: 需要注冊的事件 3 //fd: 文件描述符 4 //events: 注冊事件的類型 5 //callback: 注冊事件的回調函數 6 //arg: 注冊事件回調函數的參數 7 //事件類型有: 8 //#define EV_TIMEOUT 0x01 9 //#define EV_READ 0x02 10 //#define EV_WRITE 0x04 11 //#define EV_SIGNAL 0x08 12 //定時事件event_set(ev, -1, 0, cb, arg) 13 void 14 event_set(struct event *ev, int fd, short events, 15 void (*callback)(int, short, void *), void *arg) 16 { 17 /* Take the current base - caller needs to set the real base later */ 18 //默認為全局ev_base進行事件的注冊 19 ev->ev_base = current_base; 20 //事件回調 21 ev->ev_callback = callback; 22 //事件回調參數 23 ev->ev_arg = arg; 24 //對應文件描述符 25 ev->ev_fd = fd; 26 //事件類型 27 ev->ev_events = events; 28 //事件在活動隊列中的類型 29 ev->ev_res = 0; 30 //標識事件加入了哪個隊列 31 ev->ev_flags = EVLIST_INIT; 32 //加入活動隊列后調試的次數 33 ev->ev_ncalls = 0; 34 //Allows deletes in callback,允許在回調中刪除自己 35 ev->ev_pncalls = NULL; 36 //初始化事件在堆中的位置。剛開始為-1 37 min_heap_elem_init(ev); 38 39 /* by default, we put new events into the middle priority */ 40 //默認事件的優先級為中間 41 if(current_base) 42 ev->ev_pri = current_base->nactivequeues/2; 43 }
4.event_add
1 //事件加入隊列 2 int 3 event_add(struct event *ev, const struct timeval *tv) 4 { 5 //事件的基礎管理,事件中有一個event_base指針,指向了他所屬於的管理類 6 struct event_base *base = ev->ev_base; 7 //當前I/O復用管理,包括初始化,注冊,回調等。。。 8 const struct eventop *evsel = base->evsel; 9 //具體的I/O復用 10 void *evbase = base->evbase; 11 int res = 0; 12 13 event_debug(( 14 "event_add: event: %p, %s%s%scall %p", 15 ev, 16 ev->ev_events & EV_READ ? "EV_READ " : " ", 17 ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", 18 tv ? "EV_TIMEOUT " : " ", 19 ev->ev_callback)); 20 21 assert(!(ev->ev_flags & ~EVLIST_ALL)); 22 23 /* 24 * prepare for timeout insertion further below, if we get a 25 * failure on any step, we should not change any state. 26 */ 27 //事件的時間tv不為null並且現在事件還不在定時隊列中,我們先在小根堆中申請一個位置,以便后面加入 28 //event_set后事件的ev_flags為EVLIST_INIT 29 if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { 30 if (min_heap_reserve(&base->timeheap, 31 1 + min_heap_size(&base->timeheap)) == -1) 32 return (-1); /* ENOMEM == errno */ 33 } 34 //如果事件類型是EV_READ,EV_WRITE,EV_SIGNAL並且事件狀態不是EVLIST_INSERTED(已加入)與EVLIST_ACTIVE(已活動) 35 if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && 36 !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { 37 //將事件加入到對應的I/O復用中 38 res = evsel->add(evbase, ev); 39 if (res != -1) 40 //加入對應的I/O復用成功后,插入EVLIST_INSERTED隊列 41 event_queue_insert(base, ev, EVLIST_INSERTED); 42 } 43 44 /* 45 * we should change the timout state only if the previous event 46 * addition succeeded. 47 */ 48 //定時執行事件處理(tv不為零,表示有超時時間) 49 if (res != -1 && tv != NULL) { 50 struct timeval now; 51 52 /* 53 * we already reserved memory above for the case where we 54 * are not replacing an exisiting timeout. 55 */ 56 //定時事件已經在定時隊列中了,先從中刪除 57 if (ev->ev_flags & EVLIST_TIMEOUT) 58 event_queue_remove(base, ev, EVLIST_TIMEOUT); 59 60 /* Check if it is active due to a timeout. Rescheduling 61 * this timeout before the callback can be executed 62 * removes it from the active list. */ 63 //定時事件是否在活動隊列中,並且是定時事件,如果是,從活動隊列中刪除 64 if ((ev->ev_flags & EVLIST_ACTIVE) && 65 (ev->ev_res & EV_TIMEOUT)) { 66 /* See if we are just active executing this 67 * event in a loop 68 */ 69 //調用次數置零 70 if (ev->ev_ncalls && ev->ev_pncalls) { 71 /* Abort loop */ 72 *ev->ev_pncalls = 0; 73 } 74 //從活動隊列中刪除 75 event_queue_remove(base, ev, EVLIST_ACTIVE); 76 } 77 78 //得到當前時間 79 gettime(base, &now); 80 //更新時間 81 //當前時間點+定時事件每隔多少秒觸發時間=觸發時間點。ev->ev_timeout為事件觸發時間點 82 evutil_timeradd(&now, tv, &ev->ev_timeout); 83 84 event_debug(( 85 "event_add: timeout in %ld seconds, call %p", 86 tv->tv_sec, ev->ev_callback)); 87 //加入定時隊列 88 event_queue_insert(base, ev, EVLIST_TIMEOUT); 89 } 90 91 return (res); 92 }
5.event_base_loop
1 /* not thread safe */ 2 //默認進入全局事件管理的事件循環 3 int 4 event_loop(int flags) 5 { 6 return event_base_loop(current_base, flags); 7 } 8 //事件分發,進入事件循環,默認進入全局事件管理的事件循環 9 int 10 event_base_loop(struct event_base *base, int flags) 11 { 12 //I/O復用管理 13 const struct eventop *evsel = base->evsel; 14 //具體I/O復用 15 void *evbase = base->evbase; 16 struct timeval tv; 17 struct timeval *tv_p; 18 int res, done; 19 20 /* clear time cache */ 21 base->tv_cache.tv_sec = 0; 22 //信號處理 23 if (base->sig.ev_signal_added) 24 evsignal_base = base; 25 done = 0; 26 //事件循環 27 while (!done) { 28 /* Terminate the loop if we have been asked to */ 29 //退出 30 if (base->event_gotterm) { 31 base->event_gotterm = 0; 32 break; 33 } 34 //立即退出 35 if (base->event_break) { 36 base->event_break = 0; 37 break; 38 } 39 40 /* You cannot use this interface for multi-threaded apps */ 41 //信號處理 42 while (event_gotsig) { 43 event_gotsig = 0; 44 if (event_sigcb) { 45 res = (*event_sigcb)(); 46 if (res == -1) { 47 errno = EINTR; 48 return (-1); 49 } 50 } 51 } 52 53 //檢測時間對不對,不對的話要校准 54 timeout_correct(base, &tv); 55 //tv為當前時間 56 tv_p = &tv; 57 //如果當前事件活動隊列為0,並且事件是阻塞的,立馬到時間堆中去查找定時時間 58 if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { 59 timeout_next(base, &tv_p); 60 } else { 61 /* 62 * if we have active events, we just poll new events 63 * without waiting. 64 */ 65 //活動隊列不為空,或者此事件是非阻塞事件,將超時時間置為零,意味着沒有超時時間 66 evutil_timerclear(&tv); 67 } 68 //沒有可以執行的事件,退出 69 /* If we have no events, we just exit */ 70 if (!event_haveevents(base)) { 71 event_debug(("%s: no events registered.", __func__)); 72 return (1); 73 } 74 75 /* update last old time */ 76 //更新base的創建時間 77 gettime(base, &base->event_tv); 78 79 /* clear time cache */ 80 //清緩存 81 base->tv_cache.tv_sec = 0; 82 83 //進行對應事件的分發,將tv_p也傳入進去,tv_p為超時時間 84 res = evsel->dispatch(base, evbase, tv_p); 85 86 if (res == -1) 87 return (-1); 88 //來事件了 89 //更新緩存時間 90 gettime(base, &base->tv_cache); 91 92 //進行超時處理,處理目前時間已經到達需要執行的事件,加入活動隊列等操作 93 timeout_process(base); 94 95 //有活動隊列 96 if (base->event_count_active) { 97 //調用 98 event_process_active(base); 99 //全部執行完,並且只要執行一次,就可以跳出循環了 100 if (!base->event_count_active && (flags & EVLOOP_ONCE)) 101 done = 1; 102 } else if (flags & EVLOOP_NONBLOCK) 103 //活動隊列沒有事件,而且是非阻塞,跳出循環 104 done = 1; 105 } 106 107 /* clear time cache */ 108 base->tv_cache.tv_sec = 0; 109 110 event_debug(("%s: asked to terminate loop.", __func__)); 111 return (0); 112 }
6.timeout_next
1 //查找下一個需要處理的事件,這邊需要指針的指針,因為假如小根堆中壓根沒有事件,將指針置為空 2 static int 3 timeout_next(struct event_base *base, struct timeval **tv_p) 4 { 5 struct timeval now; 6 struct event *ev; 7 struct timeval *tv = *tv_p; 8 //查找小根堆里面的事件最小的事件,沒有就退出 9 if ((ev = min_heap_top(&base->timeheap)) == NULL) { 10 /* if no time-based events are active wait for I/O */ 11 //沒有事件了,超時時間置為空,退出,時間指針置為空,所以需要指針的指針 12 *tv_p = NULL; 13 return (0); 14 } 15 16 if (gettime(base, &now) == -1) 17 return (-1); 18 //事件已經超時,需要立即執行,清空tv_p,超時時間為0,返回 19 if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { 20 evutil_timerclear(tv); 21 return (0); 22 } 23 //事件還沒有到執行的時間,計算出相差的時間,返回 24 evutil_timersub(&ev->ev_timeout, &now, tv); 25 26 assert(tv->tv_sec >= 0); 27 assert(tv->tv_usec >= 0); 28 29 event_debug(("timeout_next: in %ld seconds", tv->tv_sec)); 30 return (0); 31 }
7.timeout_process
1 //進行時間處理 2 void 3 timeout_process(struct event_base *base) 4 { 5 struct timeval now; 6 struct event *ev; 7 //時間堆為空退出 8 if (min_heap_empty(&base->timeheap)) 9 return; 10 11 gettime(base, &now); 12 13 //事件執行時間比現在大時,需要執行,將此事件從event隊列中刪除 14 while ((ev = min_heap_top(&base->timeheap))) { 15 if (evutil_timercmp(&ev->ev_timeout, &now, >)) 16 break; 17 18 /* delete this event from the I/O queues */ 19 //從ev對應的隊列中刪除此事件 20 event_del(ev); 21 22 event_debug(("timeout_process: call %p", 23 ev->ev_callback)); 24 //發送到活動隊列,激活此事件,事件的狀態變更為EV_TIMEOUT,事件的執行次數改為1 25 event_active(ev, EV_TIMEOUT, 1); 26 } 27 }
8.event_process_active
1 //對在活動隊列中的事件調用他對應的回調 2 static void 3 event_process_active(struct event_base *base) 4 { 5 struct event *ev; 6 struct event_list *activeq = NULL; 7 int i; 8 short ncalls; 9 10 //取得第一個非空的優先級隊列,nactivequeues越小,優先級越高 11 for (i = 0; i < base->nactivequeues; ++i) { 12 if (TAILQ_FIRST(base->activequeues[i]) != NULL) { 13 activeq = base->activequeues[i]; 14 break; 15 } 16 } 17 18 assert(activeq != NULL); 19 20 for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { 21 //如果是持續事件,只從EVLIST_ACTIVE隊列中刪除事件即可 22 if (ev->ev_events & EV_PERSIST) 23 event_queue_remove(base, ev, EVLIST_ACTIVE); 24 else 25 event_del(ev); 26 27 /* Allows deletes to work */ 28 //允許刪除自己 29 ncalls = ev->ev_ncalls; 30 ev->ev_pncalls = &ncalls; 31 while (ncalls) { 32 //持續調用,直到調用次數為0 33 ncalls--; 34 ev->ev_ncalls = ncalls; 35 (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); 36 if (event_gotsig || base->event_break) 37 return; 38 } 39 } 40 }
