前面分析了libevent中对IO事件、定时器和signal的基本处理方法,也分析了libevent中基本的数据结构。从本章开始,将开始从代码结构和执行的角度,详细分析各功能的代码实现方式。
本文将主要讲述事件处理的整体整体流程。
1).event_base_new()函数源码分析:
struct event_base *
event_base_new(void)
{
int i;
struct event_base *base;
if ((base = calloc(1, sizeof(struct event_base))) == NULL)
event_err(1, "%s: calloc", __func__);
event_sigcb = NULL;
event_gotsig = 0;
detect_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;
base->evbase = NULL;
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
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);
}
这个函数是初始化libevent执行环境的函数,主要会初始化使用的event_base;主要执行流程为:
a).初始化当前时间和event_base中的时间;
b).min_heap_ctor()函数构造event_base中使用的小堆;
c).TAILQ_INIT(&base->eventqueue);初始化注册事件队列;
d).根据能找到的系统事件处理函数,注册event_base中的evsl,即系统的事件操作函数;同时,对应平台的*_init()(如epoll_init())里面,会调用int evsignal_init(struct event_base *base),该函数主要初始化信号的处理,后面信号专题里面会详细分析;
e).event_base_priority_init(base, 1); 这个函数设置event_base支持的最大事件优先级,并初始化active queue的相关结构;
2).event_base_loop()函数分析:
int
event_base_loop(struct event_base *base, int flags)
{
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);
}
这个函数是libevent处理事件的主循环, event_dispatch() 和event_loop()等函数最后都是调用这个函数来实现事件处理的主循环。
下面详细解释一下该函数的处理流程:
a).while()主循环循环处理产生的事件,当done为true时退出主循环;
b).如果base->event_gotterm或者base->event_break被设置时,退出主循环,这个当程序执行相关exit loop的函数时会被设置;
c).如果捕获到信号,则调用event_sigcb()回调函数处理信号;
d).调用timeout_correct()函数校正event_base中的时钟;
e).调用event_base->evsel->dispatch() 来调用相关平台如epoll等的event dispatch函数,如epoll_wait();在evsel->dispatch()函数中,先处理检测到的信号,然后将激活的IO事件添加到event_base的activequeue中;
f).timeout_process(base);处理所有的定时器事件,循环检测小堆中的定时器事件,如果有到期的定时器,则将事件添加到active队列中;
g).event_process_active(base);如果有激活的事件,则调用这个函数处理所有的激活队列中的事件;
3).epoll_dispatch()函数分析:
static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
struct epollop *epollop = arg;
struct epoll_event *events = epollop->events;
struct evepoll *evep;
int i, res, timeout = -1;
if (tv != NULL)
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {
/* Linux kernels can wait forever if the timeout is too big;
* see comment on MAX_EPOLL_TIMEOUT_MSEC. */
timeout = MAX_EPOLL_TIMEOUT_MSEC;
}
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
if (res == -1) {
if (errno != EINTR) {
event_warn("epoll_wait");
return (-1);
}
evsignal_process(base);
return (0);
} else if (base->sig.evsignal_caught) {
evsignal_process(base);
}
event_debug(("%s: epoll_wait reports %d", __func__, res));
for (i = 0; i < res; i++) {
int what = events[i].events;
struct event *evread = NULL, *evwrite = NULL;
int fd = events[i].data.fd;
if (fd < 0 || fd >= epollop->nfds)
continue;
evep = &epollop->fds[fd];
if (what & (EPOLLHUP|EPOLLERR)) {
evread = evep->evread;
evwrite = evep->evwrite;
} else {
if (what & EPOLLIN) {
evread = evep->evread;
}
if (what & EPOLLOUT) {
evwrite = evep->evwrite;
}
}
if (!(evread||evwrite))
continue;
if (evread != NULL)
event_active(evread, EV_READ, 1);
if (evwrite != NULL)
event_active(evwrite, EV_WRITE, 1);
}
if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
/* We used all of the event space this time. We should
be ready for more events next time. */
int new_nevents = epollop->nevents * 2;
struct epoll_event *new_events;
new_events = realloc(epollop->events,
new_nevents * sizeof(struct epoll_event));
if (new_events) {
epollop->events = new_events;
epollop->nevents = new_nevents;
}
}
return (0);
}
这个函数是epoll平台的事件dispatch函数的实现;对该函数的解释如下:
a).调用epoll_wait()函数等待产生事件;
b).在epoll_wait()执行完成后,表明程序被中断或者有到达的事件;
c).调用evsignal_process()函数先处理可能检测到的信号;
d).对于每一个被激活的事件,调用event_active()函数将事件添加到activequeue中;
本节主要分析了libevent的event_base的初始化和事件的整体处理流程,在下一节,将主要分析事件的添加和删除;