前面介绍了event,本节介绍Reactor的核心结构:event_base,它在event-internal.h中。
event_base是整个libevent的核心,它持有所有注册的事件,并负责通知激活的事件。
event_base数据结构
struct event_base {
const struct eventop *evsel;
void *evbase;
struct event_changelist changelist;
const struct eventop *evsigsel;
struct evsig_info sig;
int virtual_event_count;
int event_count;
int event_count_active;
//...
int event_running_priority;
struct event_list *activequeues;
int nactivequeues;
struct common_timeout_list **common_timeout_queues;
int n_common_timeouts;
int n_common_timeouts_allocated;
/** Mapping from file descriptors to enabled (added) events */
struct event_io_map io;
/** Mapping from signal numbers to enabled (added) events. */
struct event_signal_map sigmap;
/** All events that have been enabled (added) in this event_base */
struct event_list eventqueue;
/** Stored timeval; used to detect when time is running backwards. */
struct timeval event_tv;
/** Priority queue of events with timeouts. */
struct min_heap timeheap;
/** Stored timeval: used to avoid calling gettimeofday/clock_gettime
* too often. */
struct timeval tv_cache;
/* Notify main thread to wake up break, etc. */
/** True if the base already has a pending notify, and we don't need
* to add any more. */
int is_notify_pending;
/** A socketpair used by some th_notify functions to wake up the main
* thread. */
evutil_socket_t th_notify_fd[2];
/** An event used by some th_notify functions to wake up the main
* thread. */
struct event th_notify;
/** A function used to wake up the main thread from another thread. */
int (*th_notify_fn)(struct event_base *base);
};
略去了部分代码,看一下主要结构和逻辑:
1、eventtop是event_base的后端,它里面是函数指针,具体操作为IO复用的一些操作
struct eventop {
/** The name of this backend. */
const char *name;
void *(*init)(struct event_base *);//初始化
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);//添加
int (*dispatch)(struct event_base *, struct timeval *);//删除
int need_reinit;//是否需要再次初始化
enum event_method_feature features;
size_t fdinfo_len;
};
这些具体的操作是select、poll、epoll等,以epoll初始化eventtop结构为例(在epoll.c中)
static void *epoll_init(struct event_base *);
static int epoll_dispatch(struct event_base *, struct timeval *);
static void epoll_dealloc(struct event_base *);
static int epoll_nochangelist_add(struct event_base *base, evutil_socket_t fd,
short old, short events, void *p);
static int epoll_nochangelist_del(struct event_base *base, evutil_socket_t fd,
short old, short events, void *p);
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_nochangelist_add,
epoll_nochangelist_del,
epoll_dispatch,
epoll_dealloc,
1, /* need reinit */
EV_FEATURE_ET|EV_FEATURE_O1,
0
};
2、evbase是void指针,它用来保存IO复用的数据,指向一个数据结构。因为Libevent是跨平台的,所以数据结构不定,因此是void类型指针。
3、struct event_changelist changelist;是上次调用eventop.dispatch之后,发生事件的event的集合,它是一个结构体。
4、evsigsel是专门处理信号的IO复用结构体。
5、activequeues是指向event_list的数组,它包含着已经发生的事件,需要调用这些事件的回调函数,事件有优先级。nactivequeues指明event_list数组的长度。
6、common_timeout_queues是一个二级指针,包含超时事件,它指向超时时间的数组。
7、io、sigmap用来存储IO事件和signal事件,未必是map。
8、eventqueue存储注册到当前event_base中的event。
9、timeheap是一个小根堆,用来管理定时事件。
10、th_notify_fd[2]用来唤醒当前event_base(可能等待在epoll_wait上)。
初始化event_base
event_base_new();
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();//得到配置
if (cfg) {
base = event_base_new_with_config(cfg);//用配置cfg初始化base
event_config_free(cfg);
}
return base;
}
再来看一下event_base_new_with_config
。在这个函数中,为event_base分配了内存,初始化其内部各个变量。
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
#ifndef _EVENT_DISABLE_DEBUG_MODE
event_debug_mode_too_late = 1;
#endif
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {//为结构体event_base分配空间
event_warn("%s: calloc", __func__);
return NULL;
}
detect_monotonic();
//初始化base
gettime(base, &base->event_tv);//获取当前时间
min_heap_ctor(&base->timeheap);//初始化小根堆
TAILQ_INIT(&base->eventqueue);//初始化事件队列
//初始化与信号相关的Socketpair
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
//Socketpair,用来唤醒当前线程
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
event_deferred_cb_queue_init(&base->defer_queue);
base->defer_queue.notify_fn = notify_base_cbq_callback;
base->defer_queue.notify_arg = base;
if (cfg)
base->flags = cfg->flags;
evmap_io_initmap(&base->io);
evmap_signal_initmap(&base->sigmap);
event_changelist_init(&base->changelist);
base->evbase = NULL;
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
base->evsel = eventops[i];//给多路IO复用赋值
base->evbase = base->evsel->init(base);//选择和多路IO复用对应的数据结
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);//event是在堆上开辟的内存,使用完后要释放
return NULL;
}
if (evutil_getenv("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s", base->evsel->name);
/* allocate a single active event queue */
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
/* prepare for threading */
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
if (EVTHREAD_LOCKING_ENABLED() &&
(!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
int r;
EVTHREAD_ALLOC_LOCK(base->th_base_lock,
EVTHREAD_LOCKTYPE_RECURSIVE);
base->defer_queue.lock = base->th_base_lock;
EVTHREAD_ALLOC_COND(base->current_event_cond);
r = evthread_make_base_notifiable(base);
if (r<0) {
event_warnx("%s: Unable to make base notifiable.", __func__);
event_base_free(base);
return NULL;
}
}
#endif
return (base);
}
event_base_init()
struct event_base *
event_init(void)
{
struct event_base *base = event_base_new_with_config(NULL);
if (base == NULL) {
event_errx(1, "%s: Unable to construct event_base", __func__);
return NULL;
}
current_base = base;
return (base);
}
这个和event_base_new的区别为:1、使用的配置不同。2、设置了全局便来current_base
相关接口
1、把event和event_base关联起来。成功返回0,失败返回-1。
int event_base_set(struct event_base *, struct event *);
即设置event->ev_base=event_base,还会设置event的优先级为event_base的1/2。
2、
将event添加到event_base(event->ev_base)中。tv为超时时间,如果不为NULL,还会添加到小根堆中。成功返回0,失败返回-1。
int event_add(struct event *ev, const struct timeval *tv)
3、将event移除event_base(event->ev_base)。成功 返回0,失败返回-1。
int event_del(struct event *ev)
4、分发事件,进入event_base循环
int event_base_dispatch(struct event_base *event_base)