概述
在libevent中,事件分为3类:IO、signal和超时事件,其中IO和signal事件采用链表来管理,超时事件采用小根堆来管理;
具体实现
在此以IO事件(signal事件与其一样只需将文件描述符换为信号号即可)为例讲解libevent事件管理:
libevent中结构体struct event_base base的成员变量struct event_io_map io用于管理IO事件。
#define event_io_map event_signal_map
struct event_signal_map {
/* An array of evmap_io * or of evmap_signal *; empty entries are
* set to NULL. */
void **entries;
/* The number of entries available in entries */
int nentries;
};
该结构体成员变量void **entries我们可以理解为指针数组,而数组下标为文件描述符(或信号号)。
对于IO事件而言该数组成员变量为指向下述结构体变量的指针。
struct evmap_io {
struct event_dlist events; // 指向链表头节点
ev_uint16_t nread;
ev_uint16_t nwrite;
ev_uint16_t nclose;
};
上述结构体变量可以理解为与该文件描述符(或信号号)相关联的event的链表的入口。
libevent使用宏GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)来获取指定文件描述符(或信号号)对应的event链表入口即上述数组成员并初始化与该文件描述符(信号号)相关联的数组成员。
使用宏LIST_INSERT_HEAD(head, elm, field)将event插入到通过宏GET_IO_SLOT_AND_CTOR获取到的链表的头部。
为了便于理解请参见下图:
以上描述的是libevent将IO事件加入到libevent库中,对应的libevent接口为:
// 添加IO事件
int evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
// 添加signal事件
int evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
而后libevent调用接口event_base_dispatch(base)进入死循环,在其中调用dispatch用于获取活跃事件,调用接口event_process_active处理活跃事件对应的回调函数。
其中关于活跃事件描述见这篇博文libevent之活跃事件管理。