文章目录
一个简单libevent应用流程图如下:
由上面的流程图可以看出,我们首先需要了解struct event这个结构体。
我使用的libevent版本是:libevent-2.1.12-stable
首先了解一下event结构体
(PS:struct event_base定义在event-internal.h文件中)
struct event结构体
//event_struct.h文件
struct event {
struct event_callback ev_evcallback; //事件回调函数结构体
//采用队列来管理超时事件
/* for managing timeouts */
union {
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx; //指明该结构体在堆中的位置
} ev_timeout_pos; //仅用于定时事件处理器(event).EV_TIMEOUT类型
evutil_socket_t ev_fd;//对于I/O事件,是文件描述符;对于signal事件,是信号值
struct event_base *ev_base; //所属的event_base
//因为signal和I/O事件是不能同时设置的,所以可以使用联合来共享一个内存空间
//采用LIST_ENTRY双链表保存I/O事件和信号事件
union {
/* used for io events */
struct {
LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/* used by signal events */
struct {
LIST_ENTRY (event) ev_signal_next;
short ev_ncalls; //事件就绪时,调用ev_evcallback的次数
/* Allows deletes in callback */
short *ev_pncalls; //指针,指指向次数
} ev_signal;
} ev_;
short ev_events; //记录监听的事件类型
short ev_res; /* result passed to event callback */ //记录了当前激活事件的类型(结果传递给事件回调)
struct timeval ev_timeout; //给定时器指定超时时间
};
该结构体采用LIST_ENTRY双链表保存I/O事件和信号事件, 同一个文件描述符或者信号值对应的多个event会被连在一起(就是xxx_next成员把这些event连接起来的),所有的被加入到event_base的event也会连在一起,所有被激活的event也会被连在一起, 所以会有多个链表和队列配合使用。
在struct event中重要的还有:
struct event_callback ev_evcallback回调函数结构体
包括:
回调函数指针,传到回调函数的参数、优先级等
struct event_callback {
TAILQ_ENTRY(event_callback) evcb_active_next; //激活队列
short evcb_flags;
ev_uint8_t evcb_pri; /* smaller numbers are higher priority */
ev_uint8_t evcb_closure;
/* allows us to adopt for different types of events */
//可以采用不同类型的事件(使用联合,所以一次只能有一个回调被调用)
union {
void (*evcb_callback)(evutil_socket_t, short, void *);
void (*evcb_selfcb)(struct event_callback *, void *);
void (*evcb_evfinalize)(struct event *, void *);
void (*evcb_cbfinalize)(struct event_callback *, void *);
} evcb_cb_union;
void *evcb_arg; //传递到回调函数的参数
};
1、创建event_base:
event_base_new()
可以看出,event_base_new()是由event_base_new_with_config()实现的。
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) { //如果cfg不为NULL
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
event_base_new_with_config()
下面是此函数的源码:(有省略)
//event.c文件
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
//使用calloc()申请清零的内存区域,相当于event_base被初始化
//一次只给一个event_base分配内存
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
...
TAILQ_INIT(&base->active_later_queue);
evmap_io_initmap_(&base->io);
evmap_signal_initmap_(&base->sigmap);
event_changelist_init_(&base->changelist);
base->evbase = NULL;
...
//选择IO复用结构体
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
//确定是否应避免使用此后端(IO复用)
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;
//找到一个满足条件的IO多路复用
//evsel:指向特定后端数据的指针
base->evsel = eventops[i];
//初始化ev_base,并且会对信号监听的处理也进行初始化
base->evbase = base->evsel->init(base);
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);
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 */ //准备线程
#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE)
event_debug_created_threadable_ctx_ = 1;
#endif
#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, 0);//申请一个锁
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
#ifdef _WIN32
if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))
event_base_start_iocp_(base, cfg->n_cpus_hint);
#endif
return (base);
}
event_base_new_with_config会对event_base进行配置:
- 选择使用哪一种多路复用
- 避免使用哪种多路复用、需满足的特征
- 线程是否支持锁等
下面是几个关于event_base配置的函数:
event_config_avoid_method()
禁用某种多路复用,可以通过名称或者是 event_config的特征
int
event_config_avoid_method(struct event_config *cfg, const char *method)
{
struct event_config_entry *entry = mm_malloc(sizeof(*entry));
if (entry == NULL)
return (-1);
if ((entry->avoid_method = mm_strdup(method)) == NULL) {
mm_free(entry);
return (-1);
}
TAILQ_INSERT_TAIL(&cfg->entries, entry, next);
return (0);
}
需要满足的特征可以设置成下面几种:
enum event_method_feature {
/** Require an event method that allows edge-triggered events with EV_ET. */
EV_FEATURE_ET = 0x01, //支持边沿触发
EV_FEATURE_O1 = 0x02, //添加、删除、或者确定哪个事件激活这些动作的时间复杂度都为O(1), select、poll是不能满足这个特征的, epoll则满足
EV_FEATURE_FDS = 0x04, //支持任意的文件描述符,而不能仅仅支持套接字
EV_FEATURE_EARLY_CLOSE = 0x08 //允许使用EV_CLOSED来检测连接关闭
};
event_config_require_features()
以上的成员变量是通过该函数设置的,就是简单的赋值而已
int
event_config_require_features(struct event_config *cfg,
int features)
{
if (!cfg)
return (-1);
cfg->require_features = features;
return (0);
}
2、创建event
event是由event_new()这个函数创建的,下面看该函数的源码:
event_new()
//event.c文件
struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
{
struct event *ev;
ev = mm_malloc(sizeof(struct event));
if (ev == NULL)
return (NULL);
if (event_assign(ev, base, fd, events, cb, arg) < 0) {
mm_free(ev);
return (NULL);
}
return (ev);
}
可以看出event_new函数的工作:
只是创建一个struct event结构体指针ev,然后将它的参数原样传给event_assign()
event_assign()
int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
if (!base)
base = current_base; //current_base是一个全局变量
if (arg == &event_self_cbarg_ptr_)
arg = ev;
if (!(events & EV_SIGNAL))
event_debug_assert_socket_nonblocking_(fd);
event_debug_assert_not_added_(ev);
ev->ev_base = base;
ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_res = 0;
ev->ev_flags = EVLIST_INIT; //初始化状态
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
if (events & EV_SIGNAL) {
if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) {
event_warnx("%s: EV_SIGNAL is not compatible with "
"EV_READ, EV_WRITE or EV_CLOSED", __func__);
return -1;
}
ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
} else {
if (events & EV_PERSIST) {
evutil_timerclear(&ev->ev_io_timeout);
ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
} else {
ev->ev_closure = EV_CLOSURE_EVENT;
}
}
min_heap_elem_init_(ev);
if (base != NULL) {
/* by default, we put new events into the middle priority */
ev->ev_pri = base->nactivequeues / 2;
}
event_debug_note_setup_(ev);
return 0;
}
该函数主要是给event结构体初始化赋值:
- 绑定event_base、回调函数和回调函数参数、文件描述符以及标志位,中间判断持续化事件
如果这个event是用来监听一个信号的,那么就不能让这个event监听读或者写事件。原因是其与信号event的实现方法相抵触。
注意,此时event结构体的变量ev_flags的值是EVLIST_INIT。对变量的追踪是很有帮助的。它指明了event结构体的状态。
它通过或运算的方式取下面的值:
//event_struct.h文件
#define EVLIST_TIMEOUT 0x01 //event从属于定时器队列或时间堆
#define EVLIST_INSERTED 0x02 //event从属于注册队列
#define EVLIST_SIGNAL 0x04 //没有使用
#define EVLIST_ACTIVE 0x08 //event从属于活动队列
#define EVLIST_INTERNAL 0x10 //该event是内部使用的,信号处理时会用到
#define EVLIST_ACTIVE_LATER 0x20
#define EVLIST_FINALIZING 0x40
#define EVLIST_INIT 0x80 //event已经被初始化了
#define EVLIST_ALL 0xff
创建完一个event结构体后,就会调用event_add将event加入到event_base中,它和前面的函数一样,内部调用其他函数完成工作。
3、添加事件
event_add()
int
event_add(struct event *ev, const struct timeval *tv)
{
int res;
if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
event_warnx("%s: event has no event_base set.", __func__);
return -1;
}
EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); //加锁
res = event_add_nolock_(ev, tv, 0);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); //解锁
return (res);
}
event_add_nolock_是event_add的内部实现函数,event_add函数只是对event_base加了锁,然后调用event_add_nolock_函数完成工作。所以函数event_add是线程安全的。
event_add_nolock_()
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_BASE_ASSERT_LOCKED(base);//检查是否上锁
event_debug_assert_is_setup_(ev);
.......
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_add_(base, ev->ev_fd, ev);//加入io队列
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);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
res = 0;
}
}
......
return (res);
}
event_add_nolock_ 调用evmap_io_add_和evmap_signal_add_,把有相同文件描述符fd和信号值sig的event连在一个队列里面。
evmap_io_add_()
evmap.c文件
/* return -1 on error, 0 on success if nothing changed in the event backend,
* and 1 on success if something did. */
int
evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
const struct eventop *evsel = base->evsel;
struct event_io_map *io = &base->io;
struct evmap_io *ctx = NULL;
int nread, nwrite, nclose, retval = 0;
short res = 0, old = 0;
struct event *old_ev;
EVUTIL_ASSERT(fd == ev->ev_fd);
if (fd < 0)
return 0;
#ifndef EVMAP_USE_HT
if (fd >= io->nentries) {
if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
return (-1);
}
#endif
GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
evsel->fdinfo_len);
nread = ctx->nread;
nwrite = ctx->nwrite;
nclose = ctx->nclose;
//|= : 按位或且赋值运算符 old |= EV_READ 等同于old = old | EV_READ(先或后赋值)
if (nread)
old |= EV_READ;
if (nwrite)
old |= EV_WRITE;
if (nclose)
old |= EV_CLOSED;
//记录是不是第一次。如果是第一次,那么就说明该fd还没被加入到多路IO复用中,
if (ev->ev_events & EV_READ) {
if (++nread == 1)//则需要加入到select、epoll这些函数中
res |= EV_READ;
}
if (ev->ev_events & EV_WRITE) {
if (++nwrite == 1)
res |= EV_WRITE;
}
if (ev->ev_events & EV_CLOSED) {
if (++nclose == 1)
res |= EV_CLOSED;
}
//0xffff = 65535 = 2^16-1
if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
event_warnx("Too many events reading or writing on fd %d",
(int)fd);
return -1;
}
if (EVENT_DEBUG_MODE_IS_ON() &&
(old_ev = LIST_FIRST(&ctx->events)) &&
(old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
event_warnx("Tried to mix edge-triggered and non-edge-triggered"
" events on fd %d", (int)fd);
return -1;
}
if (res) {//把fd加入到多路IO复用中。
void *extra = ((char*)ctx) + sizeof(struct evmap_io);
/* XXX(niels): we cannot mix edge-triggered and
* level-triggered, we should probably assert on
* this. */
//这里的add()是函数指针,下面讲到struct eventop结构体的时候就能看到
if (evsel->add(base, ev->ev_fd,
old, (ev->ev_events & EV_ET) | res, extra) == -1)
return (-1);
retval = 1;
}
ctx->nread = (ev_uint16_t) nread;//把次数记录下来。
ctx->nwrite = (ev_uint16_t) nwrite;
ctx->nclose = (ev_uint16_t) nclose;
LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
return (retval);
}
nread、nwrite和nclose值为1说明是第一次监听对应的事件,需要把这个fd添加到IO多路复用函数中。
调用evsel->add()完成fd与IO多路复用函数的关联
evmap_signal_add_()
int
evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
{
const struct eventop *evsel = base->evsigsel;
struct event_signal_map *map = &base->sigmap;
struct evmap_signal *ctx = NULL;
if (sig < 0 || sig >= NSIG)
return (-1);
if (sig >= map->nentries) {
if (evmap_make_space(
map, sig, sizeof(struct evmap_signal *)) == -1)
return (-1);
}
GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
base->evsigsel->fdinfo_len);
if (LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
== -1)
return (-1);
}
LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);
return (1);
}
信号事件不能跟IO的读写事件一块监听,信号事件通过捕捉函数并创建管道(Libevent用的是 socketpair()创建一对无名、相互连接的套接字)
多路复用通过监听管道来监听事件的发生,所以信号单独采用evmap_signal_add_出来注册到base,当链表为空的时候也是用evsel->add加入多路复用监听,接着加入到链表头。
4、事件循环
事件添加完毕之后,进入event_base_dispatch函数循环处理事件。跟之前的一样,该函数内部调用event_base_loop完成工作。
event_loop()
/* not thread safe */
int
event_loop(int flags)
{
return event_base_loop(current_base, flags);
}
event_base_dispatch()
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
event_base_loop()
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
struct timeval tv;
struct timeval *tv_p;
int res, done, retval = 0;
/* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
EVBASE_ACQUIRE_LOCK(base, th_base_lock);//上锁
//如果已经是循环状态了,释放锁(只有一个event_base循环可以持有锁)
if (base->running_loop) {
event_warnx("%s: reentrant invocation. Only one event_base_loop"
" can run on each event_base at once.", __func__);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return -1;
}
base->running_loop = 1;
clear_time_cache(base);
if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base_(base);
done = 0;
#ifndef EVENT__DISABLE_THREAD_SUPPORT
base->th_owner_id = EVTHREAD_GET_ID();
#endif
base->event_gotterm = base->event_break = 0;
while (!done) {
base->event_continue = 0;
base->n_deferreds_queued = 0;
/* Terminate the loop if we have been asked to */
//如果是这些条件,就停止循环
if (base->event_gotterm) {
break;
}
if (base->event_break) {
break;
}
tv_p = &tv;
if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
/* if we have active events, we just poll new events
without waiting. */
evutil_timerclear(&tv);
}
//如果没有事件,只需退出
if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) &&
!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
}
event_queue_make_later_events_active(base);
clear_time_cache(base);
//调用IO多路复用,对event进行监听
//并且把满足条件的event放到event_base的激活队列中
res = evsel->dispatch(base, tv_p);
if (res == -1) {
event_debug(("%s: dispatch returned unsuccessfully.",
__func__));
retval = -1;
goto done;
}
update_time_cache(base);
timeout_process(base);
if (N_ACTIVE_CALLBACKS(base)) {
//遍历这个激活队列的所有event,逐个调用对应的回调函数
int n = event_process_active(base);
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) == 0
&& n != 0)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
event_debug(("%s: asked to terminate loop.", __func__));
done:
clear_time_cache(base);
base->running_loop = 0;
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (retval);
}
这里重要的是evsel->dispatch和event_process_active两个函数:
evsel->dispatch 对event进行监听,并且把满足条件的event放到event_base的激活队列中
event_process_active遍历这个激活队列的所有event,逐个调用对应的回调函数
先来看看struct eventop结构体,之前的event_add调用了evsel->add()去加入到多路复用去监听,这个evsel是struct eventop结构体指针。
struct eventop
const struct eventop *evsel = base->evsel;
//定义给定event_base的后端的结构
struct eventop {
const char *name; //后端的名称
//设置使用此后端的event_base函数
//它应该创建一个新的结构体,其中包含运行该后端所需的任何信息,并将其返回
//返回的指针将通过event_init被存储在event_base.evbase 字段中。失败时,此函数应返回 NULL
void *(*init)(struct event_base *);
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
/** As "add", except 'events' contains the events we mean to disable. */
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
//实现事件循环的核心函数:
//它必须查看哪些添加的事件已准备就绪,并且为每个活动事件(active event)调用event_active(通常通过event_io_active等)
//成功返回0,失败返回-1
int (*dispatch)(struct event_base *, struct timeval *);
//从event_base中清理和释放数据
void (*dealloc)(struct event_base *);
/** Flag: set if we need to reinitialize(重新初始化) the event base after we fork.
*/
int need_reinit;
/** Bit-array of supported event_method_features that this backend can
* provide. */
//此后端可以提供的支持event_method_features的位数组
enum event_method_feature features;
/** Length of the extra information we should record for each fd that
has one or more active events. This information is recorded
as part of the evmap entry for each fd, and passed as an argument
to the add and del functions above.
*/
//我们应该为每个具有一个或多个激活事件(active event)的fd记录其额外信息的长度
//被记录的信息作为evmap的部分进入每个fd,并作为参数传递给上面的add和del函数
size_t fdinfo_len;
};
int (*dispatch)(struct event_base *, struct timeval *);
是个函数指针,它的实现包括IO多路复用函数。
这里用select多路复用来分析:
select_dispatch
selectop结构体
//select.c文件
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
int resize_out_sets;
fd_set *event_readset_in;
fd_set *event_writeset_in;
fd_set *event_readset_out;
fd_set *event_writeset_out;
};
static int
select_dispatch(struct event_base *base, struct timeval *tv)
{
int res=0, i, j, nfds;
struct selectop *sop = base->evbase;
check_selectop(sop);
if (sop->resize_out_sets) {
fd_set *readset_out=NULL, *writeset_out=NULL;
size_t sz = sop->event_fdsz;
if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
return (-1);
sop->event_readset_out = readset_out;
if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
/* We don't free readset_out here, since it was
* already successfully reallocated(重新分配). The next time
* we call select_dispatch, the realloc will be a
* no-op(什么都不做). */
return (-1);
}
sop->event_writeset_out = writeset_out;
sop->resize_out_sets = 0;
}
memcpy(sop->event_readset_out, sop->event_readset_in,
sop->event_fdsz);
memcpy(sop->event_writeset_out, sop->event_writeset_in,
sop->event_fdsz);
nfds = sop->event_fds+1;
//解锁
EVBASE_RELEASE_LOCK(base, th_base_lock);
//调用select多路复用监听
res = select(nfds, sop->event_readset_out,
sop->event_writeset_out, NULL, tv);
//上锁
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
check_selectop(sop);
if (res == -1) {
if (errno != EINTR) {
event_warn("select");
return (-1);
}
return (0);
}
event_debug(("%s: select reports %d", __func__, res));
check_selectop(sop);
i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
for (j = 0; j < nfds; ++j) {
if (++i >= nfds)
i = 0;
res = 0;
if (FD_ISSET(i, sop->event_readset_out))
res |= EV_READ;
if (FD_ISSET(i, sop->event_writeset_out))
res |= EV_WRITE;
if (res == 0)
continue;
//把fd对应的event放到event_base的激活event队列中
evmap_io_active_(base, i, res);
}
check_selectop(sop);
return (0);
}
select返回时,如果事件发生,就调用evmap_io_active_(base, i, res),把fd对应的event放到event_base的激活event队列中。
evmap_io_active_
void
evmap_io_active_(struct event_base *base, evutil_socket_t fd, short events)
{
struct event_io_map *io = &base->io;
struct evmap_io *ctx;
struct event *ev;
#ifndef EVMAP_USE_HT
if (fd < 0 || fd >= io->nentries)
return;
#endif
GET_IO_SLOT(ctx, io, fd, evmap_io);
if (NULL == ctx)
return;
LIST_FOREACH(ev, &ctx->events, ev_io_next) {
if (ev->ev_events & events)
event_active_nolock_(ev, ev->ev_events & events, 1);
}
}
void
event_active_nolock_(struct event *ev, int res, short ncalls)
{
struct event_base *base;
......
event_callback_activate_nolock_(base, event_to_event_callback(ev));
}
int
event_callback_activate_nolock_(struct event_base *base,
struct event_callback *evcb)
{
int r = 1;
if (evcb->evcb_flags & EVLIST_FINALIZING)
return 0;
......
event_queue_insert_active(base, evcb);//最终调用这个函数加入到激活队列里
return r;
}
static void
event_queue_insert_active(struct event_base *base, struct event_callback *evcb)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (evcb->evcb_flags & EVLIST_ACTIVE) {
/* Double insertion is possible for active events */
return;
}
INCR_EVENT_COUNT(base, evcb->evcb_flags);
evcb->evcb_flags |= EVLIST_ACTIVE;
base->event_count_active++;//队列event激活数加一
MAX_EVENT_COUNT(base->event_count_active_max, base->event_count_active);
EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues);
TAILQ_INSERT_TAIL(&base->activequeues[evcb->evcb_pri],//evcb->evcb_pri指定插入哪个优先级队列
evcb, evcb_active_next);//将event插入到对应对应优先级的激活队列中
}
上面几个函数最终将fd对应的event插入到激活队列中。
因为Libevent还对事件处理有优先级,所以是激活数组队列,而不是只有一个激活队列。
处理激活列表中的event
//event-internal.h
#define N_ACTIVE_CALLBACKS(base) \
((base)->event_count_active)
当队列有被激活的event时,调用event_process_active遍历这个激活队列的所有event,逐个调用对应的回调函数。
event_process_active()
static int
event_process_active(struct event_base *base)
{
/* Caller must hold th_base_lock */
//调用者必须持有该锁
struct evcallback_list *activeq = NULL;
int i, c = 0;
const struct timeval *endtime;
struct timeval tv;
const int maxcb = base->max_dispatch_callbacks;
const int limit_after_prio = base->limit_callbacks_after_prio;
.....
for (i = 0; i < base->nactivequeues; ++i) {//从高优先级到低优先级遍历优先级数组(数字越小,优先级越高)
if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {//对于特定的优先级,遍历该优先级的所有激活event
base->event_running_priority = i;
activeq = &base->activequeues[i];
if (i < limit_after_prio)
c = event_process_active_single_queue(base, activeq,
INT_MAX, NULL);
else
c = event_process_active_single_queue(base, activeq,
maxcb, endtime);
if (c < 0) {
goto done;
} else if (c > 0)
break; /* Processed a real event; do not
* consider lower-priority events */
/* If we get here, all of the events we processed
* were internal. Continue. */
}
}
done:
base->event_running_priority = -1;
return c;
}
static int
event_process_active_single_queue(struct event_base *base,
struct evcallback_list *activeq,
int max_to_process, const struct timeval *endtime)
{
struct event_callback *evcb;
int count = 0;
EVUTIL_ASSERT(activeq != NULL);
for (evcb = TAILQ_FIRST(activeq); evcb; evcb = TAILQ_FIRST(activeq)) {
struct event *ev=NULL;
if (evcb->evcb_flags & EVLIST_INIT) {
ev = event_callback_to_event(evcb);
if (ev->ev_events & EV_PERSIST || ev->ev_flags & EVLIST_FINALIZING)//如果是持续状态事件
event_queue_remove_active(base, evcb);//仅仅从激活队列中移除
else//不是的话,那么就要把这个event删除掉。
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
.....
} else {
event_queue_remove_active(base, evcb);
event_debug(("event_process_active: event_callback %p, "
"closure %d, call %p",
evcb, evcb->evcb_closure, evcb->evcb_cb_union.evcb_callback));
}
.....
switch (evcb->evcb_closure) {
.....
case EV_CLOSURE_EVENT: {
void (*evcb_callback)(evutil_socket_t, short, void *);
short res;
EVUTIL_ASSERT(ev != NULL);
evcb_callback = *ev->ev_callback;//用户定义的回调函数
res = ev->ev_res;
EVBASE_RELEASE_LOCK(base, th_base_lock);
evcb_callback(ev->ev_fd, res, ev->ev_arg);//调用回调函数
}
break;
.....
default:
EVUTIL_ASSERT(0);
}
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
}
return count;
}
上面的代码,从高到低优先级遍历激活event优先级数组。
对于激活的event,先调用event_queue_remove_active从激活队列中删除掉,跟清除中断差不多。然后再对这个event调用其回调函数。event_queue_remove_active函数的调用会改变event结构体的ev_flags变量的值,就又可以等待下一次事件的到来了。
抛开内部的数据结构队列、链表不看
最后整个工作流程大致就是这样:
最后图看不清楚的可以私信我发给大家
若有错误,欢迎大家斧正!
参考链接:
https://blog.youkuaiyun.com/caijiwyj/article/details/106746590
https://blog.youkuaiyun.com/luotuo44/article/details/38501341