libevent中的事件机制
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 }