libevent是一个基于reactor模式的开源事件通知库,具有高性能、轻量级等优点。本文分享了对libevent 1.4源码的一些理解。
1. libevent使用实例——event-test
libevent 1.4源码提供了一些使用实例,本文通过对其中的一个实例,来分析libevent库是如何实现事件通知功能的。以下源码取自sample/event-test.c,为了便于阅读,删除了关于WIN32的代码,去掉了对返回值的错误判断。
/*
* event-test.c
* Compile with:
* cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event.h>
static void fifo_read(int fd, short event, void *arg)
{
char buf[255];
int len;
struct event *ev = arg;
/* Reschedule this event */
event_add(ev, NULL);
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
fd, event, arg);
len = read(fd, buf, sizeof(buf) - 1);
buf[len] = '\0';
fprintf(stdout, "Read: %s\n", buf);
}
int main (int argc, char **argv)
{
struct event evfifo;
struct stat st;
const char *fifo = "event.fifo";
int socket;
unlink (fifo);
mkfifo (fifo, 0600);
socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
/* Initalize the event library */
event_init();
/* Initalize one event */
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
/* Add it to the active events, without a timeout */
event_add(&evfifo, NULL);
event_dispatch();
return (0);
}
该程序会创建一个有名管道"event.fifo",并监听来自该管道的读事件。
# step1. 执行程序
./event-test
# step2. 重新打开一个shell,向管道中写入数据
$ echo "hello libevent!" >> event.fifo
# step3. 程序监听到管道上的读事件,并打印管道收到的消息内容
fifo_read called with fd: 3, event: 2, arg: 0x7ffcb6014440
Read: hello libevent!
2. 分析
2.1 event_init
event_init用来初始化一个reactor,libevent中使用结构体event_base来描述一个reactor实例,在event_init函数中,调用event_base_new函数为reactor实例分配内存并初始化,然后用一个全局的event_base指针变量(current_base)指向这块内存。
struct event_base {
const struct eventop *evsel;
... ...
};
struct eventop提供了包括初始化(init)、添加事件(add)、删除事件(del)、派发事件(dispatch)等功能,libevent提供了一张全局表eventops,在eventops中,将各种具有不同实现的eventop实例用宏进行控制,然后在event_init阶段将evsel指针指向对应的eventop实例。libevent使用了小根堆来管理定时事件。
// 如下是一个典型的eventop实例,底层基于epoll实现对事件的添加、删除、派发等
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
1 // need reinit
};
2.2 event_set
event_set函数用来初始化一个事件。libevent使用struct event来表示一个具体的事件。event_set对一个event实例进行初始化,包括设置fd、事件类型、事件处理函数及其参数。
libevent中定义的部分事件类型:
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
2.3 event_add
event_add将事件添加到reactor中。对于定时器事件,将其添加到小根堆中。对于读写事件、信号事件,使用eventop中的add函数来添加事件,然后将事件添加到reactor中的事件队列(eventqueue)中去。
2.4 event_dispatch
通过event_dispatch启动事件循环,监听事件队列中的事件,当有事件发生时,调用eventop中的dispatch函数处理事件。
event_dispatch --> event_loop(0) --> event_base_loop(current_base, 0)