赞
踩
API及调用顺序为:
event_base()初始化event_base
event_set()初始化event
event_base_set()将event绑定到指定的event_base上
event_add()将event添加到事件链表上,注册事件
event_base_dispatch()循环、检测、分发事件
我们从源码入手,一步一步剖析libevent的调用以及原理;
1.event_init()
struct event_base *
event_init(void)
{
struct event_base *base = event_base_new();
if (base != NULL)
current_base = base;
return (base);
}
event_init()函数主要调用event_base_new()函数,返回event_base结构体;其实,到后面会发现,我们直接调用event_base_new()函数也是可以的;
2.event_base_new()
struct event_base * event_base_new(void) { int i; struct event_base *base; //用calloc而不用malloc的原因? //calloc动态分配完内存后,自动初始化该内存为零 if ((base = calloc(1, sizeof(struct event_base))) == NULL) event_err(1, "%s: calloc", __func__); event_sigcb = NULL; event_gotsig = 0; detect_monotonic();//设置use_monotonic gettime(base, &base->event_tv); //初始化定时事件的小根堆 min_heap_ctor(&base->timeheap); //初始化事件链表,头 == 尾 TAILQ_INIT(&base->eventqueue); //初始化信号 base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; //初始化IO多路复用机制 base->evbase = NULL; //选取以NULL 结尾,初始化 for (i = 0; eventops[i] && !base->evbase; i++) { base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } //如果没有IO多路复用 if (base->evbase == NULL) event_errx(1, "%s: no event mechanism available", __func__); if (evutil_getenv("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s\n", base->evsel->name); /* allocate a single active event queue */ //设置优先级 //活跃事件链表中,优先级值越小,越优先 event_base_priority_init(base, 1); return (base); }
event_base_new()做的工作主要就是对结构体event_base的初始化的作用,设置一些参数这类的;
3.event_set
void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg) { /* Take the current base - caller needs to set the real base later */ //初始化中event_init() ev->ev_base = current_base; ev->ev_callback = callback; ev->ev_arg = arg; ev->ev_fd = fd; ev->ev_events = events; ev->ev_res = 0; //设置event状态 ev->ev_flags = EVLIST_INIT; ev->ev_ncalls = 0; ev->ev_pncalls = NULL; //初始化小根堆索引-1 min_heap_elem_init(ev); /* by default, we put new events into the middle priority */ if(current_base) ev->ev_pri = current_base->nactivequeues/2; }
根据初始化参数,设置event参数;
4.event_base_set()
int
event_base_set(struct event_base *base, struct event *ev)
{
/* Only innocent events may be assigned to a different base */
if (ev->ev_flags != EVLIST_INIT)
return (-1);
ev->ev_base = base;
ev->ev_pri = base->nactivequeues/2;
return (0);
}
将event_base_set()将event绑定到指定的event_base上
5.event_add()
int event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; const struct eventop *evsel = base->evsel; void *evbase = base->evbase; int res = 0; event_debug(( "event_add: event: %p, %s%s%scall %p", ev, ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); assert(!(ev->ev_flags & ~EVLIST_ALL)); /* * prepare for timeout insertion further below, if we get a * failure on any step, we should not change any state. */ if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { if (min_heap_reserve(&base->timeheap, 1 + min_heap_size(&base->timeheap)) == -1) return (-1); /* ENOMEM == errno */ } if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { res = evsel->add(evbase, ev); if (res != -1) event_queue_insert(base, ev, EVLIST_INSERTED); } /* * we should change the timout state only if the previous event * addition succeeded. */ if (res != -1 && tv != NULL) { struct timeval now; /* * we already reserved memory above for the case where we * are not replacing an exisiting timeout. */ if (ev->ev_flags & EVLIST_TIMEOUT) event_queue_remove(base, ev, EVLIST_TIMEOUT); /* Check if it is active due to a timeout. Rescheduling * this timeout before the callback can be executed * removes it from the active list. */ if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) { /* See if we are just active executing this * event in a loop */ if (ev->ev_ncalls && ev->ev_pncalls) { /* Abort loop */ *ev->ev_pncalls = 0; } event_queue_remove(base, ev, EVLIST_ACTIVE); } gettime(base, &now); evutil_timeradd(&now, tv, &ev->ev_timeout); event_debug(( "event_add: timeout in %ld seconds, call %p", tv->tv_sec, ev->ev_callback)); event_queue_insert(base, ev, EVLIST_TIMEOUT); } return (res); }
根据时间类型添加到不同的列表中;
1.将event注册到event_base的I/O多路复用要监听的事件链表中
2.将event注册到event_base的已注册事件链表中
3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中;
如果没有传入超时时间,则不会添加到小根堆中。
函数内添加到I/O多路复用监听事件链表、已注册事件链表、小根堆中都是通过event_queue_insert()完成的,相应的删除工作都是通过event_queue_remove()完成的。
6.event_base_dispatch()
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
event_base_dispatch什么也没有做,调用了event_base_loop函数;
7.event_base_loop()
int event_base_loop(struct event_base *base, int flags) { //IO复用方式 const struct eventop *evsel = base->evsel; void *evbase = base->evbase; struct timeval tv; struct timeval *tv_p; int res, done; /* clear time cache */ base->tv_cache.tv_sec = 0; if (base->sig.ev_signal_added) evsignal_base = base; done = 0; while (!done) { /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { //设置中止循环 base->event_gotterm = 0; break; } if (base->event_break) { base->event_break = 0; break; } /* You cannot use this interface for multi-threaded apps */ while (event_gotsig) { event_gotsig = 0; if (event_sigcb) { res = (*event_sigcb)(); if (res == -1) { errno = EINTR; return (-1); } } } timeout_correct(base, &tv); tv_p = &tv; if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { timeout_next(base, &tv_p); } else { /* * if we have active events, we just poll new events * without waiting. */ evutil_timerclear(&tv); } /* If we have no events, we just exit */ if (!event_haveevents(base)) { event_debug(("%s: no events registered.", __func__)); return (1); } /* update last old time */ gettime(base, &base->event_tv); /* clear time cache */ base->tv_cache.tv_sec = 0; res = evsel->dispatch(base, evbase, tv_p); if (res == -1) return (-1); gettime(base, &base->tv_cache); timeout_process(base); //有就绪事件则调用事件注册的回调函数 if (base->event_count_active) { event_process_active(base); if (!base->event_count_active && (flags & EVLOOP_ONCE)) done = 1; } else if (flags & EVLOOP_NONBLOCK) done = 1; } /* clear time cache */ base->tv_cache.tv_sec = 0; event_debug(("%s: asked to terminate loop.", __func__)); return (0); }
event_base_loop()主要就是循环、检测、分发事件
按照代码流程;
1.信号标记被设置,则调用信号的回调函数
2.根据定时器最小时间,设置I/O多路复用的最大等待时间,这样即使没有I/O事件发生,也能在最小定时器超时时返回。
3.调用I/O多路复用,监听事件,将活跃事件添加到活跃事件链表中
4.检查定时事件,将就绪的定时事件从小根堆中删除,插入到活跃事件链表中
libevent的核心就event_base_loop();在这其中检测和分发通过I/O多路复用来完成,比如我们经常使用的poll和epoll,通过epoll.c就可以看到源码;其实原理与我们之前学习到的epoll编程是很类似的,只是多了一部分的处理方式,达到与整合系统互相呼应的效果;
*libevent内部封装了很多优秀的网络接口,一起一步一步揭开她神秘的面纱~~~~*
想了解学习更多C++后台服务器方面的知识,请关注:
微信公众号:C++后台服务器开发
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。