修改记录:
3-28 初稿
3-39 补充了全局变量说明,补充了0.2的内容
libevent是一个高性能的异步处理函数库。使用libevent库的著名软件有chromium、memcached、Tor等等。
网上libevent的中文文档不是很少,其中
张亮写的libevent源码分析写得不错。但是当时的1.4版本篇幅还是过长,本文将浅析libevent的0.1及0.2版本,其中体现了libevent最基础的东西,希望能给诸位一个快速的印象。
还是请注意,现在的libevent2与之前的版本很不一样了。
基本类型
libevent最基础的数据类型是event
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; //标志
};
这里最难理解的就是TAILQ_ENTRY了,这个宏是定义在queue.h文件当中
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
从宏定义可以看出,此结构体没有名字,所以只能在某一个结构体中被使用(这里就是struct event)
这里定义的是一个tail queue的结构,该结构比较好玩,本人之前把它误想成双向链表了,但仔细看看,似乎还不全是。
tqe_next很常见,指向下一个元素
tqe_prev是 前面一个元素当中 指向下一个元素的 那个指针的指针……所以有2个星号。(在写本文之时,我还不清楚为什么要这么绕)
回过头来看,
ev_read_next可以用来找到在队列中下一个可以准备读的套接字
ev_write_next可以用来找到在队列中下一个可以准备写的套接字
ev_timeout_next可以用来找到在队列中下一个定时器
ev_add_next可以用来找到在队列中下一个需要处理的事件
0.2版本中增加了提供多平台网络编程的包装结构
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 *);
};
全局变量
刚才说到队列,可是在使用库的时候,哪来得队列呢?
这是在event.c文件中已经定义的全局变量
TAILQ_HEAD (timeout_list, event) timequeue; //定时器队列的头结点
TAILQ_HEAD (event_wlist, event) writequeue; //写队列的头结点
TAILQ_HEAD (event_rlist, event) readqueue; //读队列的头结点
TAILQ_HEAD (event_ilist, event) addqueue; //待处理队列的头结点
其中timequeue这个定时器队列是按照结构体timeval从小到大的顺序进行排列的,后面增加事件时会提到。
int event_inloop = 0; //锁。用于判断程序此时是否在轮询之中
int event_fds; /* Highest fd in fd set */
int event_fdsz; //size。用于计算event_fds总共要用多大空间
fd_set *event_readset; //对应于select的readset
fd_set *event_writeset; //对应于select的writeset
0.2版本中增加的全局变量
struct eventop *evsel;
void *evbase;
event.c当中存在一个全局数组struct eventop *eventops[]用于指示系统当中有以下哪些网络处理函数可使用:kqueue, epoll, poll, select等等。
然后程序会选择一个传给evsel
evsel会调用初始化函数返回一个指向网络处理函数结构体的指针给evbase。