libevent0.2源码分析

系统架构分析

event.c和event.h   用于定义event结构体及其相关函数
kqueue.c和select.c 用于定义eventop(消息处理的模板类)的具体实现细节
sample/event-test.c 测试实例
mising/queue/sys/queue.c 定义QUEUE库
mising/time/sys/time.c 定义TIME库

event状态图

libevent0.2状态图

libevent核心策略模式

使用eventop作为策略模板,定义了add、dispatch等接口.

1
2
3
4
5
6
7
8
9
10
# event.h
struct eventop {
char *name;
void *(*init)(void);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*recalc)(void *, int);
int (*dispatch)(void *, struct timeval *);
};

使用select等实现具体接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# select.h
void *select_init __P((void));
int select_add __P((void *, struct event *));
int select_del __P((void *, struct event *));
int select_recalc __P((void *, int));
int select_dispatch __P((void *, struct timeval *));
struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_recalc,
select_dispatch
};
//同时使用了selectop辅助管理select
struct selectop {
int event_fds; // 最大的文件描述符用于select接口
int event_fdsz;
fd_set *event_readset;
fd_set *event_writeset;
} sop;

将模式和实现结合起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# event.c
struct eventop *evsel;
void *evbase;
struct eventop *eventops[] = {
#ifdef HAVE_KQUEUE
&kqops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
NULL
};
void event_init(void)
{
evbase = NULL;
for (i = 0; eventops[i] && !evbase; i++) {
evsel = eventops[i]; //将evsel模板变量绑定到select的具体实现
evbase = evsel->init();
}
}


类型与变量

libevent最基础的数据类型是event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct event {
TAILQ_ENTRY (event) ev_read_next;
TAILQ_ENTRY (event) ev_write_next;
TAILQ_ENTRY (event) ev_timeout_next;
TAILQ_ENTRY (event) ev_add_next;
int ev_fd; //包含的socket套接字,是event的核心
short ev_events; //对应的事件集
struct timeval ev_timeout; //定时器
void (*ev_callback)(int, short, void *arg); //对应的回调函数
void *ev_arg; //参数
int ev_flags; //标志
};
  • ev_read_next可以用来找到在队列中下一个可以准备读的套接字
  • ev_write_next可以用来找到在队列中下一个可以准备写的套接字
  • ev_timeout_next可以用来找到在队列中下一个定时器
  • ev_add_next可以用来找到在队列中下一个需要处理的事件

event的状态常量

1
2
3
4
5
6
7
8
9
10
11
12
//EVLIST_* 表明event当前的状态ev_flags
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_ADD 0x08
#define EVLIST_INIT 0x80
/* EVLIST_X_ Private space: 0x1000-0xf000 */
//EV_* 表明event当前处理的事件类型ev_events
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04

timequeue、eventqueue和addqueue队列

ev_read_next、ev_write_next、ev_timeout_next和ev_add_next用于保存响应的事件链表
同时,在event.c中定义了timequeue、eventqueue和addqueue用于保存表头:

  • timequeue: 定义event超时的链表
  • eventqueue: 保存在循环中的event的链表
  • addqueue:保存待处理的event的链表
    1
    2
    3
    4
    5
    6
    7
    8
    struct event_list{
    struct event *tqh_first; /* first element */ /
    struct event **tqh_last; /* addr of last next element */ /
    }
    struct event_list timequeue;
    struct event_list eventqueue;
    struct event_list addqueue; //新创建的event,即将添加到响应链表eventqueue

TAILQ_ENTRY这个宏是定义在queue.h文件当中(可略)

1
2
3
4
5
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; //指向下一个元素
struct type **tqe_prev; //前面一个元素当中 指向下一个元素的 那个指针的指针
}

控制类

selectop为event核心控制模块的模板

1
2
3
4
5
6
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
fd_set *event_readset;
fd_set *event_writeset;
} sop;

selectop在select方式下的实现, 保存对selectop(或evsel)的各种实现操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# select.h
void *select_init __P((void));
int select_add __P((void *, struct event *));
int select_del __P((void *, struct event *));
int select_recalc __P((void *, int));
int select_dispatch __P((void *, struct timeval *));
struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_recalc,
select_dispatch
};

定义控制类evbase和evsel

1
2
3
4
# event.c
struct eventop *evsel;
void *evbase;

event.c当中存在一个全局数组struct eventop *eventops[]用于指示系统当中有以下哪些网络处理函数可使用:kqueue, epoll,poll, select等等。 然后程序会选择一个传给evsel, 并调用evsel的初始化函数返回一个指向网络处理函数结构体的指针给evbase,是具体eventop实现类的辅助工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//eventop是消息处理的模板类,kqueque和select两个文件分别实现了两个具体的类,并用evbase来存放
struct eventop *eventops[] = {
#ifdef HAVE_KQUEUE
&kqops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
NULL
};
void event_init(void)
{
int i;
TAILQ_INIT(&timequeue);
TAILQ_INIT(&eventqueue);
TAILQ_INIT(&addqueue);
evbase = NULL;
for (i = 0; eventops[i] && !evbase; i++) {
evsel = eventops[i];
evbase = evsel->init();
}
}


主要函数

test.c范例文件入手:

1
2
3
4
5
6
7
8
9
10
/* 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();

event_init进行了全体初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void event_init(void)
{
int i;
TAILQ_INIT(&timequeue);
TAILQ_INIT(&eventqueue);
TAILQ_INIT(&addqueue);
evbase = NULL;
for (i = 0; eventops[i] && !evbase; i++) {
evsel = eventops[i];
evbase = evsel->init();
}
}

除了初始化队列之外,选择第一个系统支持的函数处理程序,其顺序是kqueue, event_ports, epoll, poll,select.在找到了合适的程序支持,比如发现了select函数可以使用之后,把select的结构体selectop的地址传给evbase。

用event_set初始化一个事件

把socket以及事件、标志通通给结构体evfifo赋值

1
2
3
4
5
6
7
8
9
void event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_flags = EVLIST_INIT;
}

event_add给之前初始化的事件配置定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void
event_add(struct event *ev, struct timeval *tv)
{
...
//将event事件添加到timequeue队列中
if (tv != NULL) {
struct timeval now;
struct event *tmp;
gettimeofday(&now, NULL);
timeradd(&now, tv, &ev->ev_timeout);
if (ev->ev_flags & EVLIST_TIMEOUT)
TAILQ_REMOVE(&timequeue, ev, ev_timeout_next);
/* Insert in right temporal order */
for (tmp = TAILQ_FIRST(&timequeue); tmp;
tmp = TAILQ_NEXT(tmp, ev_timeout_next)) {
if (timercmp(&ev->ev_timeout, &tmp->ev_timeout, <=))
break;
}
if (tmp)
TAILQ_INSERT_BEFORE(tmp, ev, ev_timeout_next);
else
TAILQ_INSERT_TAIL(&timequeue, ev, ev_timeout_next);
ev->ev_flags |= EVLIST_TIMEOUT;
}
//将event事件添加到addqueue中,其中addqueue表示待加入事件,eventqueue表示待等待的事件
//addqueue主要是用于在event_inloop=1(即系统处理到期事件)
//时,临时存放的位置,之后再使用event_add_post将其放到eventqueue
if (event_inloop) {
/* We are in the event loop right now, we have to
* postpone the change until later.
*/
if (ev->ev_flags & EVLIST_ADD)
return;
TAILQ_INSERT_TAIL(&addqueue, ev, ev_add_next);
ev->ev_flags |= EVLIST_ADD;
} else
event_add_post(ev);
}

第一步是将event加入timequeue中,挂上EVLIST_TIMEOUT的标志,因为默认所有event都是可能超时的数据
第二步是判断libevent是否正在循环中,如果在而且标志了EVLIST_ADD,则返回;否则插进addqueue队尾,并标志EVLIST_ADD。可以看出EVLIST_ADD表明此event是否已经added进addqueue。我们可以把addqueue理解成待处理事件。

处理循环event_dispatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int
event_dispatch(void)
{
struct timeval tv;
struct event *ev;
int res, maxfd;
if (evsel->recalc(evbase, 0) == -1)
return (-1);
while (1) {
timeout_next(&tv);
//使用select等系统函数,检验哪些fd的消息到达,并处理。
event_inloop = 1;
res = evsel->dispatch(evbase, &tv);
event_inloop = 0;
if (res == -1)
return (-1);
//将addqueue中的event添加到eventqueue中
maxfd = 0;
for (ev = TAILQ_FIRST(&addqueue); ev;
ev = TAILQ_FIRST(&addqueue)) {
TAILQ_REMOVE(&addqueue, ev, ev_add_next);
ev->ev_flags &= ~EVLIST_ADD;
event_add_post(ev);
if (ev->ev_fd > maxfd)
maxfd = ev->ev_fd;
}
if (evsel->recalc(evbase, maxfd) == -1)
return (-1);
//处理超时的event事件
timeout_process();
}
return (0);
}

其他相关函数

1
2
3
4
5
6
7
8
9
int timeout_next(struct timeval *); //获取下一次最近延时等待的事件
void timeout_process(void); //处理延时消息
//延时事件的处理函数,使用同一的event处理函数
#define timeout_add(ev, tv) event_add(ev, tv)
#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define timeout_del(ev) event_del(ev)
#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define timeout_initalized(ev) ((ev)->ev_flags & EVLIST_INIT)

补充

event及其相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct event {
....
};
//消息队列
struct event_list timequeue;
struct event_list eventqueue;
struct event_list addqueue; //新创建的event,即将添加到响应链表eventqueue
//消息处理函数
void event_init(void);
int event_dispatch(void);
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
void event_add(struct event *, struct timeval *);
void event_del(struct event *);

libevent使用event及其函数+QUENUE库,构建一个消息管理的系统。用于消息的增删改查

eventop模板和其相关实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
fd_set *event_readset;
fd_set *event_writeset;
} sop;
void *select_init __P((void));
int select_add __P((void *, struct event *));
int select_del __P((void *, struct event *));
int select_recalc __P((void *, int));
int select_dispatch __P((void *, struct timeval *));
struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_recalc,
select_dispatch
};

eventop是消息处理的模板类,kqueque和select两个文件分别实现了两个具体的类,并用evbase来存放.libevent使用eventop模板结构体,来实现select等不同具体实现方式的兼容。其中event_*处理维护event队列的增删改查的同时,使用eventop的实现类select来完成具体的消息等待和处理过程。