libevent是一个事件通知库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
1 首先初始化一个event_base。调用event_init()
2 新生成一个事件event。设置好监听的对象,监听对象的动作,动作产生之后的回调函数,已经回调函数的参数。掉用event_set()完成。
3 设置事件所属的event_base。调用event_base_set()完成。
4 注册这个事件。主要通过event_add()完成。
a)对于定时事件,libevent使用一个小根堆管理,key为超时时间,timeheap;
b)对于I/O事件,libevent将其放入到等待链表(wait list)中,这是一个双向链表结构 eventqueue;
c)对于Signal和,libevent将其放入到sig集合中
5 程序调用event_base_dispatch()系列函数进入无限循环,等待事件,
以epoll()函数为例;
每次循环前libevent会检查定时事件的最小超时时间tv,根据tv设置select()的最大等待时间,以便于后面及时处理超时事件;
当epoll()返回后,首先检查超时事件,然后检查I/O事件;
Libevent将所有的就绪事件,放入到激活链表中;
然后对激活链表中的事件,调用事件的回调函数执行事件处理activequeues;
Reactor模式框架参考博客:
https://www.cnblogs.com/lfsblack/p/5498556.html
源码实现可以看看:https://blog.csdn.net/baidu20008/article/details/41378761
struct event_base * event_init(void)
首先初始化struct event_base 变量,并保存返回的指针。实际上这一步相当于初始化一个 Reactor 实例;在初始化 libevent 后,就可以注册事件了。
1 struct event_base。事件基础类,相当于Reactor
struct event_base {
const struct eventop *evsel; ////表示选择的事件引擎,可能为:epoll, poll, select
void *evbase; //指向IO复用机制真正存储的数据,它通过evsel成员的init函数来进行初始化
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;
/*是一个二级指针,前面讲过libevent支持事件优先级,因此你可以把它看作是数组,
* 其中的元素activequeues[priority]是一个链表, 链表的每个节点指向一个优先级为priority的就绪事件event。*/
int nactivequeues;
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue;////链表,保存了所有的注册事件event的指针。
struct timeval event_tv; //系统的当前时间
struct min_heap timeheap;////用来检测事件是否超时的堆栈,是管理定时事件的小根堆
struct timeval tv_cache;//与event::ev_timeout进行比较,确定事件是否超时
};
2 struct eventop,Libevent为了封装IO复用技术,定义了一个统一的事件操作结构体eventop。
//Libevent为了封装IO复用技术,定义了一个统一的事件操作结构体eventop:
//这里可以理解为定了了一个类eventop。 epoll、 poll、 dev/poll、 select 和 kqueue都是这个类的实例
//类的属性是name,need_reinit
//类方法是init,add,del,dispatch,dealloc
struct eventop {
const char *name; // 后端IO复用技术的名称
void *(*init)(struct event_base *);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*dispatch)(struct event_base *, void *, struct timeval *);
void (*dealloc)(struct event_base *, void *);
/* set if we need to reinitialize the event base */
int need_reinit;
};
/*epoll.c 定义实例epollops,(默认方式)
*epollops可以理解为是eventop一个实例。
* 如果是面向对象写的, eventop epollops = new eventop("epoll",epoll_init,epoll_add,epoll_del,epoll_dispatch,epoll_dealloc,)
*/
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
1 /* need reinit */
};
/*
poll.c 定义实例pollops
*/
const struct eventop pollops = {
"poll",
poll_init,
poll_add,
poll_del,
poll_dispatch,
poll_dealloc,
0
};
/*
select.c 定义实例selectops
*/
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_dispatch,
select_dealloc,
0
};
3 event_init 初始化了一个struct event_base变量,并且会选择I/O复用的具体类型。这里Linux下面默认是epoll。
struct event_base *
event_init(void)
{
struct event_base *base = event_base_new();
if (base != NULL)
current_base = base;
return (base);
}
struct event_base *
event_base_new(void)
{
int i;
struct event_base *base;
//设置base->timeheap = -1
min_heap_ctor(&base->timeheap);
TAILQ_INIT(&base->eventqueue);
//设置信号量
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
base->evbase = NULL;
//选择事件驱动类型;eventops 数组第一个元素是epoll
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i]; //&epollops
/* 指向IO复用机制真正存储的数据,它通过evsel成员的init函数来进行初始化 */
/* 比如epoll时,evbase指向的是epollop 这个地方很重要*/
base->evbase = base->evsel->init(base);
}
/* allocate a single active event queue */
event_base_priority_init(base, 1);
return (base);
}
4 base->evbase = base->evsel->init(base);在epoll方式中实际上是调用的epoll_init。
struct evepoll {
struct event *evread; //读回调事件
struct event *evwrite;//写回调事件
};
typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event
{
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
} __attribute__ ((__packed__));
struct epollop {
struct evepoll *fds; //你关注的socket数组,hash[fd] = evepoll。evepoll是事件struct event
int nfds;//您关注socket数的大小
struct epoll_event *events; //epoll_event数组
int nevents; //epoll_event数组大小默认是32
int epfd; //epoll_create(32000),你关注的socket以及socket上面发生的事件
};
static void *
epoll_init(struct event_base *base)
{
int epfd;
struct epollop *epollop;
/* Disable epollueue when this environment variable is set */
if (evutil_getenv("EVENT_NOEPOLL"))
return (NULL);
/* Initalize the kernel queue */
//函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size(32000)就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间
if ((epfd = epoll_create(32000)) == -1) {
if (errno != ENOSYS)
event_warn("epoll_create");
return (NULL);
}
FD_CLOSEONEXEC(epfd);
if (!(epollop = calloc(1, sizeof(struct epollop))))
return (NULL);
//epfd 赋值给epollop
epollop->epfd = epfd;
/* Initalize fields 初始值:32 个epoll_event*/
epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
if (epollop->events == NULL) {
free(epollop);
return (NULL);
}
epollop->nevents = INITIAL_NEVENTS;
//初始值 32 个 evepoll
epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
if (epollop->fds == NULL) {
free(epollop->events);
free(epollop);
return (NULL);
}
epollop->nfds = INITIAL_NFILES;
evsignal_init(base);
return (epollop);
}
void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg)
event_set 初始化事件event,设置回调函数和关注的事件。
参数说明:
ev:执行要初始化的event对象;
fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;
events:在该fd上关注的事件类型,它可以是EV_READ, EV_WRITE, EV_SIGNAL;
callback:这是一个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时由event_base负责传入,按顺序,实际上就是event_set时的fd, event和arg;
arg:传递给callback函数指针的参数;
定时事件说明:evtimer_set(&ev, timer_cb, NULL) = event_set(&ev, -1, 0, timer_cb, NULL)
由于定时事件不需要fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此这里event也不需要设置。
这一步相当于初始化一个event handler,在libevent中事件类型保存在event结构体中。
注意:libevent并不会管理event事件集合,这需要应用程序自行管理;
1 struct event结构体分析。
struct event {
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
unsigned int min_heap_idx; /* for managing timeouts 用于管理超时默认是-1 */
struct event_base *ev_base; //属于哪个一event_base
int ev_fd; //设置事件监听对象,也就是监听句柄
short ev_events; //设置监听对象触发的动作:EV_READ, EV_WRITE, EV_SIGNAL,EV_TIMEOUT,EV_PERSIST
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; //事件的状态,EVLIST_INIT,EVLIST_INTERNAL,EVLIST_ACTIVE,EVLIST_SIGNAL,EVLIST_INSERTED,EVLIST_TIMEOUT
};
2 event_set源码分析
void
event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
/* Take the current base - caller needs to set the real base later */
//设置当前的event_base,默认是current_base,后面可以通过event_base_set重新设置
ev->ev_base = current_base;
//设置事件的回调函数
ev->ev_callback = callback;
//设置事件的回调函数的参数
ev->ev_arg = arg;
//设置事件监听对象,也就是监听句柄
ev->ev_fd = fd;
//设置监听对象触发的动作:EV_READ, EV_WRITE, EV_SIGNAL
ev->ev_events = events;
ev->ev_res = 0;
ev->ev_flags = EVLIST_INIT; //设置事件的状态
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
min_heap_elem_init(ev);
/* by default, we put new events into the middle priority */
if(current_base)
ev->ev_pri = current_base->nactivequeues/2;
}
int event_base_set(struct event_base *base, struct event *ev)
设置event从属的event_base,这一步相当于指明event要注册到哪个event_base实例上。
int
event_base_set(struct event_base *base, struct event *ev)
{
/* Only innocent events may be assigned to a different base */
if (ev->ev_flags != EVLIST_INIT)
return (-1);
ev->ev_base = base; //设置event的event_base
ev->ev_pri = base->nactivequeues/2;
return (0);
}
int event_add(struct event *ev, const struct timeval *tv)
基本信息都已设置完成,只要简单的调用event_add()函数即可完成,其中tv是定时值;这一步相当于调用Reactor::register_handler()函数注册事件。
int
event_add(struct event *ev, const struct timeval *tv)
{
struct event_base *base = ev->ev_base; //自己事件所属的event_base
const struct eventop *evsel = base->evsel; //示选择的事件模型,这里默认是epllo
void *evbase = base->evbase;////事件模型存储数据的地址指针
int res = 0;
// tv不为0设置了,超时事件
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 */
}
//1将ev存放到evsel事件模型的数据evbase中,2 将ev插入到&base->eventqueue的末尾
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
//将ev添加到evbase(epoll:epollop)的fds中(这个地方很重要,相当调用了epoll_add)
res = evsel->add(evbase, ev);
if (res != -1)
event_queue_insert(base, ev, EVLIST_INSERTED); //添加到base->eventqueue的末尾
}
}
epoll_add 函数中,主要是向内核注册监听事件。
static int
epoll_add(void *arg, struct event *ev)
{
struct epollop *epollop = arg;
struct epoll_event epev = {0, {0}};
struct evepoll *evep;
int fd, op, events;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(ev));
fd = ev->ev_fd;
if (fd >= epollop->nfds) {
/* Extent the file descriptor array as necessary */
if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
return (-1);
}
//给fd分配一个epollop。hash[fd] = evep。
evep = &epollop->fds[fd];
op = EPOLL_CTL_ADD;
events = 0;
//读
if (evep->evread != NULL) {
events |= EPOLLIN;
op = EPOLL_CTL_MOD;
}
//写
if (evep->evwrite != NULL) {
events |= EPOLLOUT;
op = EPOLL_CTL_MOD;
}
//读事件
if (ev->ev_events & EV_READ)
events |= EPOLLIN;
//写事件
if (ev->ev_events & EV_WRITE)
events |= EPOLLOUT;
epev.data.fd = fd;
epev.events = events;
/* epoll_ctl:epoll的事件注册函数
* epollop->epfd:由 epoll_create 生成的epoll专用的文件描述符
* op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
* ev->ev_fd:监听的对象,也就是监听事件的句柄。
* epev:指向epoll_event的指针。主要包含2部分信息:epev.data.fd = 监听的对象。
* epev.events = events监听对象的事件类型读或者写。
*/
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
return (-1);
/* Update events responsible */
if (ev->ev_events & EV_READ)
evep->evread = ev; //读回调处理ev
if (ev->ev_events & EV_WRITE)
evep->evwrite = ev; //写回调处理ev
return (0);
}
int event_base_loop(struct event_base *base, int flags)
程序进入无限循环,等待就绪事件并执行事件处理
event_base *base:event_base
flags:超时时间。
1 event_base_loop,主要是通过evsel->dispatch(base, evbase, tv_p),真实调用了epoll_dispatch(),epoll_dispatch()函数中要是是调用了内核的epoll_wait,epoll_wait是阻塞等待关注的有事件发生。如果有关注时间的发生之后调用event_active将事件写入到event_base的激活事件队列中。如果有激活的队列,调用event_process_active(base)去执行激活的队列。
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;//网络I/O模型,epoll
void *evbase = base->evbase;//模型中数据存储,epollop
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) {
/*没有注册的事件,就退出 */
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
return (1);
}
// 调用系统I/O demultiplexer等待就绪I/O events,可能是epoll_wait,或者select等;
// 在evsel->dispatch()中,会把就绪signal event、I/O event插入到激活链表中
//epoll模型,主要调用的是epoll_dispatch
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
timeout_process(base);
// 调用event_process_active()处理激活链表中的就绪event,调用其回调函数执行事件处理
// 该函数会寻找最高优先级(priority值越小优先级越高)的激活事件链表,
// 然后处理链表中的所有就绪事件;
// 因此低优先级的就绪事件可能得不到及时处理;
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;
return (0);
}
2 epoll_dispatch()函数,主要是函数中要是是调用了内核的epoll_wait,epoll_wait是阻塞等待关注的有事件发生。如果有关注时间的发生之后调用event_active将事件写入到event_base的激活事件列表中。
static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
struct epollop *epollop = arg;
struct epoll_event *events = epollop->events;
struct evepoll *evep;
int i, res, timeout = -1;
if (tv != NULL)
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {
/* Linux kernels can wait forever if the timeout is too big;
* see comment on MAX_EPOLL_TIMEOUT_MSEC. */
timeout = MAX_EPOLL_TIMEOUT_MSEC;
}
/* 等待事件触发,当超过timeout还没有事件触发时,就超时
* epfd:由epoll_create 生成的epoll专用的文件描述符;
* epoll_event:用于回传代处理事件的数组;
* maxevents:每次能处理的事件数;
* timeout:等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
* res:返回发生事件数
*
* epoll_wait工作原理:
* poll_wait运行的原理是
* 等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。
* 并 且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话
* ,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。
* 这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
*/
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
if (res == -1) {
if (errno != EINTR) {
event_warn("epoll_wait");
return (-1);
}
evsignal_process(base);
return (0);
} else if (base->sig.evsignal_caught) {
evsignal_process(base);
}
for (i = 0; i < res; i++) {
int what = events[i].events;
struct event *evread = NULL, *evwrite = NULL;
int fd = events[i].data.fd; //拿到触发事件的socketId
if (fd < 0 || fd >= epollop->nfds)
continue;
//从epollop->fds[fd]找到回调evep。epollop->fds[fd] = evep,hash结构。
evep = &epollop->fds[fd];
//获取读写回调event
if (what & (EPOLLHUP|EPOLLERR)) {
evread = evep->evread;
evwrite = evep->evwrite;
} else {
if (what & EPOLLIN) {
evread = evep->evread;
}
if (what & EPOLLOUT) {
evwrite = evep->evwrite;
}
}
if (!(evread||evwrite))
continue;
//将读写放到激活队列中
if (evread != NULL)
event_active(evread, EV_READ, 1);
if (evwrite != NULL)
event_active(evwrite, EV_WRITE, 1);
}
//扩容
if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
/* We used all of the event space this time. We should
be ready for more events next time. */
int new_nevents = epollop->nevents * 2;
struct epoll_event *new_events;
new_events = realloc(epollop->events,
new_nevents * sizeof(struct epoll_event));
if (new_events) {
epollop->events = new_events;
epollop->nevents = new_nevents;
}
}
return (0);
}
3 event_active函数,主要是讲激活的event加入激活队列中:base->activequeues。
//放入到激活队列中,ev:事件,res:EV_READ|EV_WRITE,ncalls:调用次数:1
void
event_active(struct event *ev, int res, short ncalls)
{
/* We get different kinds of events, add them together */
if (ev->ev_flags & EVLIST_ACTIVE) {
ev->ev_res |= res;
return;
}
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}
4 event_process_active主要是处理激活队列中的数据
static void
event_process_active(struct event_base *base)
{
struct event *ev;
struct event_list *activeq = NULL;
int i;
short ncalls;
////获得就绪链表中有就绪事件并且高优先级的表头
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
activeq = base->activequeues[i];
break;
}
}
assert(activeq != NULL);
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
if (ev->ev_events & EV_PERSIST)
event_queue_remove(base, ev, EVLIST_ACTIVE);
else
event_del(ev);//如果不是永久事件则需要进行一系统的删除工作,包括移除注册在事件链表的事件等
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
//根据回调次数调用回调函数
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
if (event_gotsig || base->event_break)
return;
}
}
}