文章目录
  1. event_add函数
  2. 向事件多路分发器注册事件
  3. eventop结构体
    1. fdinfo_len
    2. I/O复用技术的优先级

event_add函数

创建一个event对象的函数为将event_new封装后的函数evsignal_new、evtimer_new等;event_new的实现比较简单,为event对象分配内存并初始化其部分成员。

创建了event对象后,应用程序调用event_add函数将其添加到注册事件队列中,并将相应的事件注册到事件多路分发器上。

event_add函数在event.c文件中实现,主要调用内部函数event_add_nolock_,外加调用前后两次加锁。

关于函数event_add_nolock_的分析详见代码清单12-3。

其中调用了几个重要的函数。

  • evmap_io_add:将I/O事件添加到事件多路分发器中,并将对应的事件处理器添加到I/O事件队列中,同时建立I/O事件与I/O事件队列之间的映射关系;
  • evmap_signal_add:将信号事件添加到事件多路分发器中,并将对应的事件处理器添加到信号事件队列中,同时建立信号事件与信号事件队列之间的映射关系;
  • event_queue_insert:将事件处理器添加到各种事件队列中,即:将I/O事件处理器与信号事件处理器插入注册事件队列;将定时器插入通用定时器队列或时间堆;将被激活的事件处理器添加到活动事件队列中;

向事件多路分发器注册事件

本节具体介绍evmap_io_add与evmap_signal_add这两个函数。在此之前,先介绍要用到的重要的数据结构。这部分详见代码清单12-5。

eventop结构体

eventop结构体封装了I/O复用机制必要的一些操作,如注册事件、等待事件等。它为event_base支持的所有后端I/O复用机制提供了统一的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
引用方式: #include <event.h>
name: 后端I/O复用技术的名称
init: 初始化函数接口
add: 注册事件接口
del: 删除事件接口
dispatch: 等待事件接口
dealloc: I/O资源释放接口
need_reinit: 程序调用fork后是否需要重新初始化event_base
features: I/O复用技术支持的特性; EV_FEATURE_ET: 支持边沿触发事件EV_ET || EV_FEATURE_O1: 事件检测算法的复杂度为O(1) || EV_FEATURE_FDS: 不仅能监听socket文件上的事件, 还能监听其他类型的文件描述符上的事件
fdinfo_len: 详见fdinfo_len段
*/
struct eventop {
const char *name;
void *(*init)(struct event_base *);
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*dispatch)(struct event_base *, struct timeval *);
void (*dealloc)(struct event_base *);
int need_reinit;
enum event_method_feature features;
size_t fdinfo_len;
};

fdinfo_len

有的I/O复用机制需要为每个I/O事件队列与信号事件队列分配额外的内存,以避免同一个文件描述符被重复插入I/O复用机制的事件表中。于是在evmap_io_add/del在调用eventop的add/del方法时,将这段内存的起始地址作为第五个参数传递给add/del方法,fdinfo_len则用来指定这段内存的长度。

I/O复用技术的优先级

event ports > kqueue > epoll > /dev/poll > poll > select。

可见,在Linux下libevent默认选择epoll I/O复用技术。