上一节我们具体分析了关于event
结构, 其中涉及到了event_base
所以本节就来分析与event
同样重要的结构. event_base
也就是Reactor模式中的反应器, 就是管理整个事件的注册, 删除等主要操作.
该结构在event-internal.h
中.
struct event_base {
const struct eventop *evsel; // 选择事件的方法. 如epoll就调用关于epoll的几个函数
void *evbase; // 具体IO多路复用所需的成员, 定义成 void* 完全就是保证统一, 毕竟每一个IO多路复用需要参数的类型都可能不同
// 注册的事件总数
int event_count; /* counts number of total events */
// 就绪事件的总数
int event_count_active; /* counts number of active events */
// 退出标识位,如果置为1了,那么就会退出事件主循环,为0则不会
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; // 添加事件到所有的 event 中
struct timeval event_tv; // 定时器
struct min_heap timeheap; // 小根堆的定时器
struct timeval tv_cache; // 用于与 event.ev_timeout 对比, 判断是否超时
};
是不是还是很懵逼, 这跟event
一样有很多成员, 而且不同成员表示不同事件的处理. 这里还是以比较难以理解的进行解释吧, evsel和evbase我们下面具体分析.
event
结构中有三个链表吗, 不记得可以回event结构看一下, 但是在event中链表的长度其实我们并不知道, 而他们的长度就在event_base
这两个参数来判断的.event
结构中设置, 通过设置ev_pri
. 所以一个事件的优先级所在链表就是 activequeues[event->ev_pri]
.(这里大家不懂多理一下, 一定要理解).activequeues
设置的优先级个数. 如: nactivequeues = 2, 表示一共有两个优先级队列. 默认为 1.event
一样, 对信号有特殊处理. (这个成员以后分析信号的时候再来分析)以上成员解释现在最重要是弄清楚activequeues
就行. 现在我们依照上一节的过程理一下event_base
的处理过程.
事件注册 .
如果事件被注册在event
中并完成了对event
结构初始化后, 接着将初始化event_base
. 将event_count
+1 . 如果事件是信号, 那么对结构中sig
执行一系列操作(现在不必清楚); 如果事件设置了定时, 那么将它设置好结构中的定时器参数.
事件到来
事件到来后将事件加入到event
的就绪队列中同时将还要加入对应优先级队列activequeues
链表中, event_count_active
+ 1 . 如果是定时的, 通过比较 event_base->tv_cache
和event->ev_timeout
比较是否超时, 超时则加入到就绪队列中.
创建一个event_base对象就是创建一个新的libevent实例. 而前面也说到过Reactor的主要框架就是事件的注册, 删除等. 而这些功能的实现需要根据不同的IO多路复用函数不同处理, 但是尽管调用函数不同但是在libevent
中都将他们融合成了统一的接口函数了. 他们分别是 :
int event_add(struct event *ev, const struct timeval *timeout); // 注册事件
int event_del(struct event *ev); // 删除事件
int event_base_loop(struct event_base *base, int loops); // 执行事件主循环
void event_active(struct event *event, int res, short events); // 激活事件
void event_process_active(struct event_base *base); // 执行激活的事件
现在将他们展示出来就是希望你们能有一个主观上的了解, 以后分析至少能够联系其他有哪些功能函数.
咱们先来看一下eventop
结构长什么样, 满足好奇心
// 选择具体某种 IO多路复用 来事件处理的结构
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;
};
实际上eventop
就是将我们几种IO多路复用(poll, epoll等)函数封装成统一的函数调用. 这些复用函数的选择是由libevent自动完成的. 这个现在有一个印象就行了, 以后分析IO多路复用时再来具体分析.
本节咱们分析了event_base
, 但是可能第一次看得时候还是不太清楚, 不要慌, 等你对libevent大致都看了一遍之后再回过头看这些脉络就清晰了. 下面我们就来分析反应器接口函数的部分函数实现.