事件处理框架event_base
事件处理框架对应于Reactor模式中的Reactor管理器,提供了事件注册、注销等接口。
- 事件处理框架对应event_base结构体,这也是Libevent的核心。
struct event_base {
// Libevent选择的io多路复用模型
const struct eventop* evsel;
//调用i/o模型evsel->init()返回的变量,相当于io多路复用模型上下文
void* evbase;
//当前注册的事件总数
int event_count; /* counts number of total events */
//激活事件总数
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop 正常退出dispatch*/
int event_break; /* Set to terminate loop immediately 马上退出dispatch*/
/* active event management */
// 一个二级指针,等同于一个数组,数组中的每个元素都指向一个链表,链表的每个元素都指向优先级为priority的激活事件
struct event_list** activequeues;
// 优先级队列数
int nactivequeues;
/* signal handling info */
struct evsignal_info sig; //信号相关
// 保存所有的注册事件
struct event_list eventqueue;
struct timeval event_tv;
// 管理注册的定时事件的小根堆
struct min_heap timeheap;
// 用于时间管理的变量
struct timeval tv_cache;
};
- event_base的初始化:使用event_base_new函数,主要功能是初始化小根堆、初始化保存注册事件的队列、初始化IO多路复用上下文、初始化一个优先级队列,这个队列保存相同优先级的事件。
- 事件的注册:事件处理框架提供事件注册接口event_add,用于注册EV_READ、EV_WRITE、EV_TIMEOUT事件,事件注册完以后,会放入相应的保存注册事件的队列中。如下所示:
/**
* ev:使用event_set()接口进行初始化好的事件
* tv:等待事件发生的时长
*/
int
event_add(struct event* ev, const struct timeval* tv)
{
// 事件所属的event_base
struct event_base* base = ev->ev_base;
// Libevent选择的io多路复用模型
const struct eventop* evsel = base->evsel;
// io多路复用模型上下文
void* evbase = base->evbase;
int res = 0;
/*
* prepare for timeout insertion further below, if we get a
* failure on any step, we should not change any state.
*/
// tv不为空,则准备注册超时事件
//预先在二叉堆中预留一个空位给新添加的超时事件
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 */
}
// IO读写或者信号事件未插入已注册或者已激活队列中,则
// 1. 首先对于IO读写事件,将调用底层的epoll_ctrl将套接字文件描述符挂载到epoll实例上;
// 对于信号事件则设置信号处理器,并且向epoll注册监听EV_READ | EV_PERSIST的ev_signal事件
// 2. 将IO读写事件和ev_signal事件插入到对应的注册队列中
if ((ev->ev_events & (EV_READ | EV_WRITE | EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED | EVLIST_ACTIVE))) {
res = evsel->add(evbase, ev);
if (res != -1)
event_queue_insert(base, ev, EVLIST_INSERTED);
}
/*
* we should change the timout state only if the previous event
* addition succeeded.
*/
if (res != -1 && tv != NULL) {
struct timeval now;
/*
* we already reserved memory above for the case where we
* are not replacing an exisiting timeout.
*/
// 如果定时器已经在定时器小根堆中,删除旧的
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
// 如果定时器在激活队列中,立即将其从active队列移除
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
// 得到定时器触发时间点
gettime(base, &now);
evutil_timeradd(&now, tv, &ev->ev_timeout);
event_debug((
"event_add: timeout in %ld seconds, call %p",
tv->tv_sec, ev->ev_callback));
// 插入定时器到小根堆中
event_queue_insert(base, ev, EVLIST_TIMEOUT);
}
return (res);
}
- 事件的注销:event_del接口
事件处理主循环
事件处理主循环将根据操作系统提供的IO多路复用机制执行事件主循环,对已经注册的就绪事件(有事件发生)调用注册事件时指定的事件处理程序来处理事件。
- Libevent的事件主循环主要通过event_base_loop函数完成。
int
event_base_loop(struct event_base* base, int flags)
{
// Libevent选择的IO多路复用机制
const struct eventop* evsel = base->evsel;
// io多路复用模型上下文
void* evbase = base->evbase;
struct timeval tv;
struct timeval* tv_p;
int res, done;
/* clear time cache */
base->tv_cache.tv_sec = 0;
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
base->event_gotterm = 0;
break;
}
if (base->event_break) {
base->event_break = 0;
break;
}
// 校正系统时间
timeout_correct(base, &tv);
// epoll_wait的阻塞时间
tv_p = &tv;
// 无激活事件并且未设置EVLOOP_NONBLOCK标志
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
//获取到base->timeheap最小堆中最先超时的计时器,用于计算epoll_wait等待事件就绪的时长
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv); // 将阻塞超时时间清零
}
/* If we have no events, we just exit */
//事件循环中没有要监听的事件退出
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
return (1);
}
/* update last old time */
gettime(base, &base->event_tv);
/* clear time cache */
base->tv_cache.tv_sec = 0;
// 调用epoll_wait等待事件就绪,
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
// base->tv_cache为当前系统时间
gettime(base, &base->tv_cache);
//处理已经触发的计时器事件,通过获取小根堆堆顶与当前时间比较
//如果堆顶时间小于当前时间说明计时器已经触发,将event插入到
//base->activequeues激活队列,将从小根堆中删除
timeout_process(base);
// active队列(激活事件队列)有事件则统一进行处理回调
if (base->event_count_active) {
event_process_active(base);
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
/* clear time cache */
base->tv_cache.tv_sec = 0;
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)