本文将从一个使用libevent的小例子出发,解释libevent处理事件的流程.
例子如下:
01.staticvoid fifo_read(intfd, short event, void *arg) {...} 02. 03.intmain (int argc, char **argv) 04.{ 05.intsocket = open ("/tmp/event.fifo", O_RDONLY | O_NONBLOCK, 0); 06. 07.fprintf(stdout,"Please write data to %s\n", fifo); 08. 09.event_init(); 10. 11.structevent evfifo; 12.event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); 13. 14.event_add(&evfifo, NULL); 15. 16.event_dispatch(); 17.}
libevent库的使用方法大体上就像例子展示的那样,先由event_init()得到一个event_base实例(也就是反应堆实例),然后由 event_set()初始化一个event,接着用event_add()将event绑定到event_base,最后调用event_dispatch()进入时间主循环.
event_init()和event_set()功能都很简单,它们分别对event_base结构体和event结构体做初始化.我们直接看看event_add():
01.//为了思路清晰,这里分析的是I/O事件,暂不考虑信号和定时器相关处理代码. 02. 03.//函数将ev注册到ev->ev_base上,事件类型由ev->ev_events指明.如果注册成功,ev将被插入到已注册链表中. 04.intevent_add(struct event *ev, const struct timeval *tv) 05.{ 06.//得到ev对应的反应堆实例event_base 07.structevent_base *base = ev->ev_base; 08. 09.//得到libevent选择的已封装的I/O多路复用技术 10.conststruct eventop *evsel = base->evsel; 11. 12.void*evbase = base->evbase; 13.intres = 0; 14. 15.//ev->ev_events表示事件类型 16.//如果ev->ev_events是 读/写/信号 事件,而且ev不在 已注册队列 或 已就绪队列, 17.//那么调用evbase注册ev事件 18.if((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && 19.!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) 20.{ 21. 22.//实际执行操作的是evbase 23.res = evsel->add(evbase, ev); 24. 25.//注册成功,把事件ev插入已注册队列中 26.if(res != -1) 27.event_queue_insert(base, ev, EVLIST_INSERTED); 28.} 29. 30.return(res); 31.}
注释已经很明了了,如果一个事件不在已注册队列或者已激活队列,而且它是I/O事件或者信号事件,那么调用select_add()将ev插入到selectop的内部数据结构中(本文以select为例,下文不再说明.).select_add()代码如下:
01.//略去信号处理的相关代码 02. 03.staticint select_add(void*arg, struct event *ev) 04.{ 05.structselectop *sop = arg; 06. 07.//如果是读类型事件,把该事件的文件描述符加入到selectop维护的读fd_set集 08.//event_readset_in中,并且把该事件插入到读事件队列. 09.if(ev->ev_events & EV_READ) 10.{ 11.FD_SET(ev->ev_fd, sop->event_readset_in); 12.sop->event_r_by_fd[ev->ev_fd] = ev; 13.} 14. 15.//略去对写事件的处理 16.}
小结一下,结合event_add()代码和select_add()代码,可以看出在调用event_add()时,事件将被插入其对应的反应堆实例event_base的已注册事件队列中,而且还会被加入到selectop维护的内部数据结构中进行监视.
现在可以看看event_dispatch()代码了:
01.//略去信号事件和定时器事件处理的相关代码 02. 03.intevent_dispatch(void) 04.{ 05.return(event_loop(0)); 06.} 07. 08.intevent_loop(int flags) 09.{ 10.returnevent_base_loop(current_base, flags); 11.} 12. 13.//事件主循环 14.intevent_base_loop(structevent_base *base, intflags) 15.{ 16.conststruct eventop *evsel = base->evsel; 17.void*evbase = base->evbase; 18.structtimeval *tv_p; 19.intres, done; 20. 21.done = 0; 22.while(!done) 23.{ 24.//从定时器最小堆中取出根节点,其时间值作为select最大等待时间 25.//如果定时器最小堆没有元素,那么select最大等待时间为0 26.timeout_next(base, &tv_p); 27. 28.//调用select_dispatch(),它会将已经准备好的事件移到已就绪事件队列中 29.res = evsel->dispatch(base, evbase, tv_p); 30. 31.//有就绪事件了,那就处理就绪事件吧. 32.if(base->event_count_active) 33.event_process_active(base); 34.} 35.}
event_base_loop()先从定时器最小堆中取出根节点作为select的最大等待时间,然后调用select_dispatch()将已经准备好的事件移到已就绪事件队列中,最后调用event_process_active()处理已就绪事件队列.
01.//略去信号事件和定时器事件处理的相关代码
02.
03.staticint select_dispatch(structevent_base *base, void*arg, struct timeval *tv)
04.{
05.intres, j;
06.structselectop *sop = arg;
07.
08.//根据前面对select_add()的解释,事件fd已被加入到fd_set集中进行监视.
09.res = select(sop->event_fds + 1, sop->event_readset_out,
10.sop->event_writeset_out, NULL, tv);
11.
12.for(j = 0, res = 0; j <= sop->event_fds; ++j, res = 0)
13.{
14.structevent *r_ev = NULL, *w_ev = NULL;
15.
16.//找出已经准备好读的事件
17.if(FD_ISSET(j, sop->event_readset_out))
18.{
19.r_ev = sop->event_r_by_fd[i];
20.res |= EV_READ;
21.}
22.
23.//将已经准备好读的事件移到已就绪事件队列
24.if(r_ev && (res & r_ev->ev_events))
25.event_active(r_ev, res & r_ev->ev_events, 1);
26.
27.//略去对已经准备好写的事件的处理
28.}
29.}
看看在event_base_loop()中被调用的event_process_active()代码:
01.staticvoid event_process_active(structevent_base *base)
02.{
03.structevent *ev;
04.structevent_list *activeq = NULL;
05.inti;
06.shortncalls;
07.
08.//寻找最高优先级(priority值越小优先级越高)的已就绪事件队列
09.for(i = 0; i < base->nactivequeues; ++i)
10.{
11.if(TAILQ_FIRST(base->activequeues[i]) != NULL)
12.{
13.activeq = base->activequeues[i];
14.break;
15.}
16.}
17.
18.for(ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq))
19.{
20.//如果有persist标志,则只从激活队列中移除此事件,
21.if(ev->ev_events & EV_PERSIST)
22.event_queue_remove(base, ev, EVLIST_ACTIVE);
23.
24.else//否则则从激活事件列表,以及已注册事件中双杀此事件
25.event_del(ev);
26.
27.ncalls = ev->ev_ncalls;
28.ev->ev_pncalls = &ncalls;
29.
30.//每个事件的回调函数的调用次数
31.while(ncalls)
32.{
33.ncalls--;
34.ev->ev_ncalls = ncalls;
35.
36.//调用回调函数
37.(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
38.}
39.}
40.}
现在,看看这个被阉割的只考虑I/O事件的libevent主循环框架:
event_base_loop: while() { //从定时器最小堆取出select最大等待时间 //select出已准备事件,将它们移到已就绪事件队列中 //处理已就绪事件 }
原文:http://blog.csdn.net/yusiguyuan/article/details/19614491