现在已经知道,libevent有三种事件类型,分别是时钟事件,信号事件,i/o事件。今天就分析一下信号事件,下面是一个简单的信号事件demo
#include <sys/types.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <sys/stat.h> #ifndef WIN32 #include <sys/queue.h> #include <unistd.h> #include <sys/time.h> #else #include <windows.h> #endif #include <signal.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <event.h> int called = 0; static void signal_cb(int fd, short event, void *arg) { struct event *signal = arg; printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal)); if (called >= 2) event_del(signal); called++; } int main (int argc, char **argv) { struct event signal_int; /* Initalize the event library */ struct event_base* base = event_base_new(); /* Initalize one event */ event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int); event_base_set(base, &signal_int); event_add(&signal_int, NULL); event_base_dispatch(base); event_base_free(base); return (0); }
从代码看,这里event_set第二个参数是一个中断类型的信号(ctrl+c可触发),第三个参数代表这是一个信号事件并长存
event_add的代码如下
1 int 2 event_add(struct event *ev, const struct timeval *tv) 3 { 4 struct event_base *base = ev->ev_base; 5 const struct eventop *evsel = base->evsel; 6 void *evbase = base->evbase; 7 int res = 0; 8 9 event_debug(( 10 "event_add: event: %p, %s%s%scall %p", 11 ev, 12 ev->ev_events & EV_READ ? "EV_READ " : " ", 13 ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", 14 tv ? "EV_TIMEOUT " : " ", 15 ev->ev_callback)); 16 17 assert(!(ev->ev_flags & ~EVLIST_ALL)); 18 19 /* 20 * prepare for timeout insertion further below, if we get a 21 * failure on any step, we should not change any state. 22 */ 23 if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) 24 { 25 if (min_heap_reserve(&base->timeheap, 26 1 + min_heap_size(&base->timeheap)) == -1) 27 return (-1); /* ENOMEM == errno */ 28 } 29 30 if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && 31 !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { 32 res = evsel->add(evbase, ev); 33 if (res != -1) 34 event_queue_insert(base, ev, EVLIST_INSERTED); 35 } 36 37 /* 38 * we should change the timout state only if the previous event 39 * addition succeeded. 40 */ 41 if (res != -1 && tv != NULL) { 42 struct timeval now; 43 44 /* 45 * we already reserved memory above for the case where we 46 * are not replacing an exisiting timeout. 47 */ 48 if (ev->ev_flags & EVLIST_TIMEOUT) 49 event_queue_remove(base, ev, EVLIST_TIMEOUT); 50 51 /* Check if it is active due to a timeout. Rescheduling 52 * this timeout before the callback can be executed 53 * removes it from the active list. */ 54 if ((ev->ev_flags & EVLIST_ACTIVE) && 55 (ev->ev_res & EV_TIMEOUT)) { 56 /* See if we are just active executing this 57 * event in a loop 58 */ 59 if (ev->ev_ncalls && ev->ev_pncalls) { 60 /* Abort loop */ 61 *ev->ev_pncalls = 0; 62 } 63 64 event_queue_remove(base, ev, EVLIST_ACTIVE); 65 } 66 67 gettime(base, &now); 68 evutil_timeradd(&now, tv, &ev->ev_timeout); 69 70 event_debug(( 71 "event_add: timeout in %ld seconds, call %p", 72 tv->tv_sec, ev->ev_callback)); 73 74 event_queue_insert(base, ev, EVLIST_TIMEOUT); 75 } 76 77 return (res); 78 }
第五行,代表的是当前系统所支持的后端模式,base->evsel和base->evbase先不要纠结,都是用在一个地方
第23行关于最小堆的逻辑先跳过
第30行,当事件ev不在已注册或者激活链表中,则调用evbase注册事件,这里的ev_events,ev_flags分别代表event关注的事件类型与当前的状态
ev_events有四种类型
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号: EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
ev_flags有以下几种状态
#define EVLIST_TIMEOUT 0x01 // event在time堆中 #define EVLIST_INSERTED 0x02 // event在已注册事件链表中 #define EVLIST_SIGNAL 0x04 // 未见使用 #define EVLIST_ACTIVE 0x08 // event在激活链表中 #define EVLIST_INTERNAL 0x10 // 内部使用标记 #define EVLIST_INIT 0x80 // event已被初始化
重点分析一下res = evsel->add(evbase, ev);我所在为win32平台,实际调用的是int win32_insert(void *op, struct event *ev)
1 int 2 win32_insert(void *op, struct event *ev) 3 { 4 struct win32op *win32op = op; 5 struct event_entry *ent; 6 7 if (ev->ev_events & EV_SIGNAL) { 8 if (win32op->signals_are_broken) 9 return (-1); 10 return (evsignal_add(ev)); 11 } 12 if (!(ev->ev_events & (EV_READ|EV_WRITE))) 13 return (0); 14 ent = get_event_entry(win32op, ev->ev_fd, 1); 15 if (!ent) 16 return (-1); /* out of memory */ 17 18 event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd)); 19 if (ev->ev_events & EV_READ) { 20 if (do_fd_set(win32op, ent, 1)<0) 21 return (-1); 22 ent->read_event = ev; 23 } 24 if (ev->ev_events & EV_WRITE) { 25 if (do_fd_set(win32op, ent, 0)<0) 26 return (-1); 27 ent->write_event = ev; 28 } 29 return (0); 30 }
今天先看到这,接下来看调用win32_init() 内部初始化了socket_pair,win32_dispatch(),内部调用了select