> 文章列表 > 事件处理框架

事件处理框架

事件处理框架

一、事件处理框架-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_TIMEOUTint 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_TIMEOUTif (ev->ev_flags & EVLIST_TIMEOUT) {event_queue_remove_timeout(base, ev);}
#endifgettime(base, &now);common_timeout = is_common_timeout(tv, base);
#ifdef USE_REINSERT_TIMEOUTwas_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_TIMEOUTevent_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);elseres = 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);
}