本文主要分析一下event_base相关的数据结构和操作接口。
1、先看一下event_base结构:
struct event_base {
const struct eventop *evsel;
void *evbase;
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* active event management */
struct event_list **activequeues;
int nactivequeues;
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue;
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
};
相关成员解释如下:
a).const struct eventop *evsel; 这个成员是对应平台的事件处理函数,该结构的定义如下:
struct eventop {
const char *name;
void *(*init)(struct event_base *);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*dispatch)(struct event_base *, void *, struct timeval *);
void (*dealloc)(struct event_base *, void *);
/* set if we need to reinitialize the event base */
int need_reinit;
};
在event_base_new()函数中,会根据对应的平台注册相应的事件处理函数,其中我们在linux平台主要会分析epollops结构中的定义; 该结构用于将libevent的事件处理与select、epoll和kqueue等事件处理关联起来;
b).void* evbase; 这个为libevent对应的平台事件处理的event base,对于epoll就是epollop结构的指针,具体想了解的可以去阅读epoll相关的书籍;由于需要兼容不同的平台,所以需要存储转化为void* 指针;
c).int event_count; 是所用的已经注册的事件的总数;
d).int event_count_active; 当前被激活等待处理的事件;
e).int event_gotterm; 该变量用于标识是否会退出event_loop,会等到事件处理函数里面才退出;
f).int event_break; 该变量被设置时,会立即退出event_loop;
g).struct event_list** activequeques; 是被激活的事件的队列,每个event_loop中,处理该队列中所有的事件,调用对应的回调函数; 这个结构是一个队列数组,数组中的每一项为激活事件队列的头指针,而数组中的index则表示事件的优先级,最低index 的事件队列会最优先被处理;
h).int nactivequeues; 这个表示active_list的总优先级数;
i).struct evsignal_info sig; 是用于处理信号的结构,下面会详细分析这个结构;
j).struct event_list eventqueque; 是所有已注册的事件的队列,每次event_loop都会wait其中所有的事件,并将其中有事件到达的event'添加到active queue中去;
k).struct timeval event_tv; 这个用于记录当前的事件,用于进行定时相关的处理;
l).struct min_heap timeheap; 一个小堆,用于管理所有的定时器事件;
m).struct timeval tv_cache; 没有太大用途,不分析;
2、struct evsignal_info 结构的分析:
struct evsignal_info {
struct event ev_signal;
int ev_signal_pair[2];
int ev_signal_added;
volatile sig_atomic_t evsignal_caught;
struct event_list evsigevents[NSIG];
sig_atomic_t evsigcaught[NSIG];
#ifdef HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max;
};
该结构主要用于进行信号的处理,具体参数的意义说明如下:
a).struct event_ev_signal; 是用于监听是否有信号发生的事件,这个事件会绑定socketpair[0];
b).int ev_signal_pair[2]; 是用于处理信号的一对socket pair;
c).int ev_signal_added; 表明是否已经注册过信号处理的事件,即上面的ev_signal事件是否已经被注册过;
d).volatile sig_atomic_t evsignal_caught; 类似于一个bool变量,表示是否捕获到信号的发生;
e).struct event_list evsigevents[NSIG]; 是注册的对应信号的event, 数组下标为产生的信号,每个数组都是一个event_list,其中是该信号所有已经注册的event,从这个地方我们可以看到,一个signal是可以注册多个处理函数的;
f).sig_atomic_t evsigcaught[NSIG]; 是一个数组,其中每个slot对应一个信号,用于表示是否产生了该信号;
g).下面的old相关的都是用于在循环结束时恢复老的信号处理函数;
在下面的文章中,将从处理流程的角度分别介绍相关事件处理的流程。