一、事件处理框架-event_base
Reactor模式有几个基础的组件,其中的Reactor框架就是event_base结构体,下面是定义(只列出部分常用的成员),如下:
struct event_base {
/* 后台使用的I/O多路复用机制 */
const struct eventop *evsel;
/* 指向struct epollop对象的指针 */
void *evbase;
/**
* 激活队列(数组)用来保存激活事件对应的回调函数。每个事件有一个event_callback类型的成员ev_evcallback,用来保存事件对应的回调
* 当事件被触发时,会把事件的回调保存到激活队列。激活队列是一个优先级队列,下标越小优先级越高。
*/
struct evcallback_list *activequeues;
/* activequeues 数组的长度 */
int nactivequeues;
/* 公共超时队列 */
struct common_timeout_list **common_timeout_queues;
/* 公共超时队列的实际长度 */
int n_common_timeouts;
/* 公共超时队列的容量 */
int n_common_timeouts_allocated;
/* 保存注册的I/O事件 */
struct event_io_map io;
/* 保存注册的信号事件 */
struct event_signal_map sigmap;
/* 这是一个最小堆,用来保存注册的超时事件 */
struct min_heap timeheap;
};
二,创建和初始化event_base
1、创建默认的event_base
创建一个 event_base 对象也既是创建了一个新的 libevent 实例,event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。一般情况下整个默认的event_base 对象就能满足需求。
2、创建复杂的event_base
如果要创建复杂的event_base 对象就需要使用 event_config,下面是一些常用的接口
struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);
先调用 event_config_new()分配一个event_config。 然后,对event_config调用其它函数,设置所需要的event_base特征。最后,调用 event_base_new_with_config()创建新的 event_base。完成创建工作后,使用event_config_free()释放event_config。下面是设置event_base特征的接口
接口:int event_config_avoid_method(struct event_config *cfg, const char *method);
功能:让libevent避免使用特定的I/O多路复用机制
示例:
/* 不使用select与poll机制 */
event_config_avoid_method(cfg, "select");
event_config_avoid_method(cfg, "poll");
接口:int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
功能:让libevent使用所指定特征的I/O多路复用机制
示例:
enum event_method_feature {
/* 要求支持边沿触发的I/O多路复用机制 */
EV_FEATURE_ET = 0x01,
/* 要求添加、删除单个事件或者确定哪个事件激活的操作是 O(1)复杂度的I/O多路复用机制 */
EV_FEATURE_O1 = 0x02,
/* 要求支持任意文件描述符,而不仅仅是套接字的I/O多路复用机制 */
EV_FEATURE_FDS = 0x04,
};
接口:int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
功能:libevent 在创建 event_base 时设置一个或者多个运行时标志
示例:
enum event_base_config_flag {
/* 不要为 event_base 分配锁,设置这个选项可以 为 event_base 节省一点用于锁定和解锁的时间,但是让在多个线程中访问 event_base 成为不安全的 */
EVENT_BASE_FLAG_NOLOCK = 0x01,
EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
三,接口函数
Reactor 框架的作用就是提供事件的注册、注销接口;根据系统提供的事件多路分发机制执行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数来处理事件。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);
1、事件注册
函数原型:int event_add(struct event *ev, const struct timeval *tv)
参数:
ev:指向要注册的事件
tv:超时时间
函数功能:函数将 ev 注册到 ev->ev_base 上,事件类型由 ev->ev_events 指明,如果注册成功,ev将被插入到已注册链表中;如果 tv 不是 NULL,则会同时注册定时事件,将 ev 添加到 timer堆上。下面是具体的代码实现,如下:
/* ****************************************************************************
函 数 名 : event_add_nolock_
功能描述 : 注册事件
输入参数 : ev : 要注册的事件
tv : 超时时间
tv_is_absolute : 用来判断传入的时间是绝对时间还是相对时间,1:绝对时间;0:相对时间(即:是一个时长,例如:2s)
返 回 值 : 成功 0, 失败 -1
**************************************************************************** */
int event_add_nolock_(struct event *ev, const struct timeval *tv,
int tv_is_absolute)
{
struct event_base *base = ev->ev_base;
int res = 0;
int notify = 0;
/* 如果event设置了超时时间,并且event所设超时结构体不在time小根堆上,则在time小根堆中预留空间 */
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 */
}
/* 如果是I/O事件或信号事件,并且事件ev不在已注册或者激活链表中 */
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
/* 如果是I/O事件,把事件添加到I/O链表 */
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_add_(base, ev->ev_fd, ev);
/* 如果是信号事件,把事件添加到信号链表 */
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
if (res != -1)
event_queue_insert_inserted(base, ev);
/* res == 1 说明监控的事件有变更 */
if (res == 1) {
/* 需要通知main thread,即:运行事件循环的线程,也就是调用event_base_loop的线程 */
notify = 1;
res = 0;
}
}
/* 准备添加定时事件 */
if (res != -1 && tv != NULL) {
struct timeval now;
int common_timeout;
#ifdef USE_REINSERT_TIMEOUT
int was_common;
int old_timeout_idx;
#endif
/* 如果业务侧把这个事件设置成EV_PERSIST(即:永久事件),并且是相对超时时间(即:设置的是一个超时时长) */
if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
/* 记录业务侧设置的超时时长 */
ev->ev_io_timeout = *tv;
#ifndef USE_REINSERT_TIMEOUT
if (ev->ev_flags & EVLIST_TIMEOUT) {
event_queue_remove_timeout(base, ev);
}
#endif
gettime(base, &now);
common_timeout = is_common_timeout(tv, base);
#ifdef USE_REINSERT_TIMEOUT
was_common = is_common_timeout(&ev->ev_timeout, base);
old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif
/* 如果是绝对时间 */
if (tv_is_absolute) {
ev->ev_timeout = *tv;
} else if (common_timeout) {
/* 超时时间在公共队列 */
struct timeval tmp = *tv;
tmp.tv_usec &= MICROSECONDS_MASK;
evutil_timeradd(&now, &tmp, &ev->ev_timeout);
/* 公共超时时间带上标志 */
ev->ev_timeout.tv_usec |= (tv->tv_usec & ~MICROSECONDS_MASK);
} else {
/* 设置的是一个超时时长,并且超时时长不是common-timeout */
/* now表示的是当前时间,tv是超时时间,ev->ev_timeout保存的就是超时后的时间 */
evutil_timeradd(&now, tv, &ev->ev_timeout);
}
#ifdef USE_REINSERT_TIMEOUT
event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else
/* 将超时事件插入到超时队列(common-timeout 队列或小根堆) */
event_queue_insert_timeout(base, ev);
#endif
/* 如果是一个公共超时时间 */
if (common_timeout) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
/* 如果插入的事件是队列的第一个事件,注册一个超时事件 */
if (ev == TAILQ_FIRST(&ctl->events)) {
common_timeout_schedule(ctl, &now, ev);
}
} else {
struct event* top = NULL;
if (min_heap_elt_is_top_(ev))
/* 如果本次插入的是最小的,就需要唤醒主线程,告诉其最小超时值已经变了 */
notify = 1;
/* 如果小根堆堆顶的节点(即:拥有最小超时时间的节点)对应的超时时间小于当前时间,说明当前堆顶的节点已经超时 */
else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
evutil_timercmp(&top->ev_timeout, &now, <))
notify = 1;
}
}
/* 如果当前线程不是主线程且注册事件成功,就唤醒主线程 */
if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
event_debug_note_add_(ev);
return (res);
}
2、事件删除
函数原型:int event_del(struct event *ev);
参数:
ev:要删除的事件
函数功能:该函数将删除事件 ev,对于 I/O 事件,从 I/O 的 demultiplexer 上将事件注销;对于 Signal事件,将从 Signal 事件链表中删除;对于定时事件,将从堆上删除。下面是具体的代码实现,如下:
int event_del_nolock_(struct event *ev, int blocking)
{
struct event_base *base;
int res = 0, notify = 0;
/* An event without a base has not been added */
if (ev->ev_base == NULL)
return (-1);
base = ev->ev_base;
EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
/* 如果事件在超时队列中 */
if (ev->ev_flags & EVLIST_TIMEOUT) {
event_queue_remove_timeout(base, ev);
}
/* 如果事件已经激活 */
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove_active(base, event_to_event_callback(ev));
else if (ev->ev_flags & EVLIST_ACTIVE_LATER)
event_queue_remove_active_later(base, event_to_event_callback(ev));
/* 如果事件已经添加到队列 */
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove_inserted(base, ev);
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_del_(base, ev->ev_fd, ev);
else
res = evmap_signal_del_(base, (int)ev->ev_fd, ev);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
res = 0;
}
if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base))
notify = 1;
}
/* if we are not in the right thread, we need to wake up the loop */
if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
event_debug_note_del_(ev);
return (res);
}