尝试着解释几个重要的函数,首先是event_inint()函数,这个函数式最初就需要使用的函数,此函数初始化了一个全局的变量current_base,也就是event_base,默认情况下所有的event都是在想这个变量中添加。
struct event_base *event_init(void) { struct event_base *base = event_base_new(); if (base != NULL) current_base = base; return (base); }函数体貌似很简单,也就是初始化了一个变量,current_base,这个是全局变量,这个函数也是提供给用户的接口函数,在应用程序中,可以使用它的返回值,也可以不用返回值,不用返回值的话,在以后的所有event变量都是天价到current_base变量中,有返回值的目的是应用程序中可以多次调用event_init()函数,也就是产生多个struct event_base,可以将不同的event添加到不同的evet_base中。这个函数中主要的是调用了event_base_new(),下面继续分析event_base_new()函数:
struct event_base * 174 event_base_new(void) 175 { 176 int i; 177 struct event_base *base; 178 179 if ((base = calloc(1, sizeof(struct event_base))) == NULL) 180 event_err(1, "%s: calloc", __func__); 181 182 event_sigcb = NULL; 183 event_gotsig = 0; 184 185 detect_monotonic(); 186 gettime(base, &base->event_tv); 187 188 min_heap_ctor(&base->timeheap); 189 TAILQ_INIT(&base->eventqueue); 190 base->sig.ev_signal_pair[0] = -1; 191 base->sig.ev_signal_pair[1] = -1; 192 193 base->evbase = NULL; 194 for (i = 0; eventops[i] && !base->evbase; i++) { 195 196 base->evsel = eventops[i]; 197 198 base->evbase = base->evsel->init(base); 199 } 200 201 if (base->evbase == NULL) 202 event_errx(1, "%s: no event mechanism available", __func__); 203 204 if (getenv("EVENT_SHOW_METHOD")) 205 event_msgx("libevent using: %s\n", 206 base->evsel->name); 207 208 /* allocate a single active event queue */ 209 event_base_priority_init(base, 1); 210 211 return (base); 212 }这个函数很多中的很多处理都是为了为多次调用event_init()设计的,实现动态申请空间,(这里关于信号的一切都不多讲,在后面有专门的讲解),188行是小顶堆的初始化,189是事件队列的初始化(eventqueue是链表,保存了所有的注册事件event的指针,这个链表貌似是循环链表) 193~199选择合适的I/O复用机制,这个选择是系统在编译的时候已经设置好的,首先是给base->evbase设为空,然后查找eventops中不为空的选项,然后赋值,在以后只要evbase不为空,即使eventops不为空也不在修改evbase的值。这是因为eventop是有权限之分的。最后一部分呢:就是初始化base中的activequeues了: 同时还需要注意198行,这个是已经选择的I/O复用机制中所写的初始化函数,这里不介绍具体的内容,在稍后的更加详细的内容中介绍这个过程。
int 321 event_base_priority_init(struct event_base *base, int npriorities) 322 { 323 int i; 324 325 if (base->event_count_active) 326 return (-1); 327 328 if (base->nactivequeues && npriorities != base->nactivequeues) { 329 for (i = 0; i < base->nactivequeues; ++i) { 330 free(base->activequeues[i]); 331 } 332 free(base->activequeues); 333 } 334 335 /* Allocate our priority queues */ 336 base->nactivequeues = npriorities; 337 base->activequeues = (struct event_list **)calloc(base->nactivequeues, 338 npriorities * sizeof(struct event_list *)); 339 if (base->activequeues == NULL) 340 event_err(1, "%s: calloc", __func__); 341 342 for (i = 0; i < base->nactivequeues; ++i) { 343 base->activequeues[i] = malloc(sizeof(struct event_list)); 344 if (base->activequeues[i] == NULL) 345 event_err(1, "%s: malloc", __func__); 346 TAILQ_INIT(base->activequeues[i]); 347 } 348 349 return (0); 350 }
325行首先判断是够链表存在,如果存在就不再做一下的操作,event_count_active是事件活跃的数目,nactivequeues是队列的数目,其实这个数值就是优先级的数值,不如只用一个优先级,那么就只有一个队列,有三个优先级就必须初始化三个队列,但是avtivequeues永远都是只需要一个即可,328~333首先是释放原始的链表元素,是为了重新初始化新的链表初始化链表元素做准备。释放二级链表的时候的特色也需要注意。释放之后重新申请从336开始。
申请的队列个数就是优先级的个数,首先申请二级链表,然后再进行复制,每个元素都是以一个struct event_list,并且每申请好一个都需要做相应的初始化,也就是346行,函数体这么处理也是为了为应用程序重新修正优先级做准备,如果已经有被激活的事件,那么就不能再重新申请配置,如果尚且没有活跃事件,那么还可以继续调整优先级。一切从新开始,对外的接口函数可以是
int event_priority_init(int npriorities);这个函数体中只用一句话
int 315 event_priority_init(int npriorities) 316 { 317 return event_base_priority_init(current_base, npriorities); 318 }关于event_init()函数已经基本结束,接下来是别的几个重要函数