一、接触libevent
libevent是用C语言编写的开源高性能网络库,是学习网络编程、后台开发必备的源码学习资料。其源代码相当精炼、易读,设计技巧和技术框架也很精彩,所以在此我也将我的理解与思考与大家分享,后期我自己也模仿实现了C++版本的网络库,欢迎一起来讨论。
二、libevent特点
-高性能,体现在使用了事件驱动的设计模式,即Reactor模型。
-跨平台,支持目前几乎所有操作系统。
-支持多种I/O多路复用技术epoll、poll、select、kqueue等,并在此基础上进行封装,提供统一接口。
-将I/O、定时器、信号三者事件集成统一。
三、Reactor模式
提到网络编程,不得不提主流的也是使用最广泛的Reactor设计模式(另一个是Preactor模式),那么什么是Reactor,翻译过来是“反应堆”,它不同于传统的顺序执行逻辑,而是运用了回调函数的技术,应用程序将相应的接口注册到Reactor上,如果有相应的事件发生,Reactor将主动调用应用程序注册的接口来实现功能。举个例子:用手机订外卖,订完可以干其他事,外卖好了自然有骑手送上门,也就意味着可以开吃了,而不用去一遍遍去咨询商家外卖是否做好。如下图是Reactor模型的架构图。
其中,Reactor作为所有事件的管理组件,支持事件的注册与注销,内部通过调用操作系统的I/O多路复用技术进行事件循环,当有事件到达时,调用相应的Event Handler。
那么libevent的事件处理流程是怎样的?
1)由应用程序初始化event,设置句柄、事件类型和回调函数;
2)向libevent注册该事件event,libevent会将I/O事件和Signal事件放入到等待链表(双向链表)中进行管理,对于定时事件,libevent使用小根堆来管理。
3)程序调用event_base_dispatch()函数进入无限循环,等待事件就绪。libevent会将所有就绪事件放入到激活链表中,最终调用事件的回调函数执行事件处理。
四、核心代码
1.event
struct event {
/*TAILQ_ENTRY是宏定义的双向链表节点结构
ev_next和ev_signal_next分别I/O事件和信号事件在链表中的位置
ev_active_next指向该事件在激活事件链表中的位子*/
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
//min_heap_idx表示该事件在小根堆中的索引
unsigned int min_heap_idx; /* for managing timeouts */
struct event_base *ev_base;
int ev_fd; //表示文件描述符或信号
/*事件类型,EV_WRITE\EV_READ\EV_TIMEOUT\EV_SIGNAL\EV_PERSIST
可通过|或运算组合*/
short ev_events;
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout;
int ev_pri; /* smaller numbers are higher priority */
void (*ev_callback)(int, short, void *arg); //回调函数
void *ev_arg;
int ev_res; /* result passed to event callback */
int ev_flags; //标记event当前状态
};
struct event_base {
const struct eventop *evsel; //指向全局变量static const struct eventop *eventops[]中的一个
void *evbase; //eventop实例对象
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* active event management */
struct event_list **activequeues; //二级指针——activequeues[priority]
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;
};
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_DEVPOLL
&devpollops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
在编译阶段,libevent会根据操作系统按执行效率选择所支持的复用机制,如Linux下的epoll、poll、select等。
在event_base对象初始化时,会将evsel指向eventops中的一个并实例化一个该对象。3.事件循环
libevent的事件主循环通过event_base_loop()函数完成,其流程如下图所示。
对应得代码如下
int event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
struct timeval *tv_p;
int res, done;
/* 清空事件缓存 */
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); //校正系统时间
/* 根据timer heap中事件的最小超时时间,设置I/O demultiplexer最大等待时间 */
tv_p = &tv;
if (!base->event_count_active && !(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 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;
/*调用系统I/O demultiplexer等待就绪事件
evsel->dispatch会将就绪事件插入到激活链表中*/
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
timeout_process(base);
//根据优先级处理就绪事件
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);
}
五、总结
这篇文章主要介绍总结了libevent的整体框架与Reactor模型,也从代码层面分析了libevent主要数据结构与执行逻辑。下一篇会介绍libevent事件的集成。