Libevent源码分析-event_base

时间:2022-11-01 00:04:11

前面介绍了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)