转自http://www.cnblogs.com/secondtonone1/p/5535722.html
最近自学libevent事件驱动库,参考的资料为libevent2.2版本以及张亮提供的《Libevent源码深度剖析》,
参考资料:http://blog.csdn.net/sparkliang/article/details/4957667
libevent好处之类的就不赘述了,libevent和libiop,redis等一样都是采用事件回调机制,这种模式
被称作Reactor模式。正常事件处理流程是应用程序调用某个接口触发某个功能,而Reactor模式需要
我们将这些接口和宿主指针(谁调用这些接口)注册在Reactor,在合适的时机Reactor使用宿主指针
一: Reactor基本知识
Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:
1)响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/
进程的切换开销;
3)可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 cpu 资源;
4)可复用性, reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性;
Reactor模式框架
1) Handle 意思为句柄,在Linux表示文件描述符,在windows是socket或者handle。
2)EventDemultiplexer 表示事件多路分发机制,调用系统提供的I/O多路复用机制,
比如select,epoll,程序先将关注的句柄注册到EventDemultiplexer上,当有关注的事件
到来时,触发EventDemultiplexer通知程序,程序调用之前注册好的回调函数完成消息
相应。对应到 libevent 中,依然是 select、 poll、 epoll 等,但是 libevent 使用结构体eventop
进行了封装,以统一的接口来支持这些 I/O 多路复用机制,达到了对外隐藏底层系统机制的目的。
3)Reactor——反应器
Reactor,是事件管理的接口,内部使用 event demultiplexer 注册、注销事件;并运行事
件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。
对应到 libevent 中,就是 event_base 结构体。
一个典型的Reactor声明方式
class Reactor
{
public:
int register_handler(Event_Handler *pHandler,int event);
int remove_handler(Event_Handler *pHandler,int event);
void handle_events(timeval *ptv);
// ...
};
4) Event Handler——事件处理程序
事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供 Reactor 在相应的
事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。
对应到 libevent 中,就是 event 结构体。
下面是两种典型的 Event Handler 类声明方式, 二者互有优缺点。
7
class Event_Handler
{
public:
virtual void handle_read() = 0;
virtual void handle_write() = 0;
virtual void handle_timeout() = 0;
virtual void handle_close() = 0;
virtual HANDLE get_handle() = 0;
// ...
};
class Event_Handler
{
public:
// events maybe read/write/timeout/close .etc
virtual void handle_events(int events) = 0;
virtual HANDLE get_handle() = 0;
// ...
};
二:如何使用libevent库提供的API
1)首先初始化 libevent 库,并保存返回的指针
struct event_base * base = event_init();
实际上这一步相当于初始化一个 Reactor 实例;在初始化 libevent 后,就可以注册事件了。
调用函数void event_set(struct event *ev,int fd,short event,void (*cb)(int,
short,void *),void *arg);
每个参数的意义:
ev:执行要初始化的 event 对象;
fd:该 event 绑定的“句柄”,对于信号事件,它就是关注的信号;
event:在该 fd 上关注的事件类型,它可以是 EV_READ,EV_WRITE,EV_SIGNAL;
cb:这是一个函数指针,当 fd 上的事件 event 发生时,调用该函数执行处理,它有三个参数,
分别是关注的fd,关注的事件类型(读/写/信号),回调函数的参数void* arg,调用时由
event_base 负责传入,按顺序,实际上就是 event_set 时的 fd,event 和 arg;
arg:传递给 cb 函数指针的参数;
由于定时事件不需要 fd,并且定时事件是根据添加时( event_add)的超时值设定的,因此
这里 event 也不需要设置。
这一步相当于初始化一个 event handler,在 libevent 中事件类型保存在 event 结构体中。
注意: libevent 并不会管理 event 事件集合,这需要应用程序自行管理;
3)设置 event 从属的 event_base
event_base_set(base,&ev);
这一步相当于指明 event 要注册到哪个 event_base 实例上;
4)将事件添加到事件队列里
event_add(&ev,timeout);
基本信息都已设置完成,只要简单的调用 event_add()函数即可完成,其中 timeout 是定时值;
10
这一步相当于调用 Reactor::register_handler()函数注册事件。
5)程序进入无限循环,等待就绪事件并执行事件处理
event_base_dispatch(base);
看一下libevent提供的sample
int main(int argc,char **argv) { struct event evfifo; #ifdef WIN32 HANDLE socket; /* Open a file. */ socket = CreateFileA("test.txt",0); line-height:1.5!important"> open File */ GENERIC_READ, open for reading */ 0,0); line-height:1.5!important"> do not share */ NULL,0); line-height:1.5!important"> no security */ OPEN_EXISTING,0); line-height:1.5!important"> existing file only */ FILE_ATTRIBUTE_NORMAL,0); line-height:1.5!important"> normal file */ NULL); no attr. template */ if (socket == INVALID_HANDLE_VALUE) return 1; #else struct stat st; const char *fifo = event.fifo"; int socket; if (lstat(fifo,&st) == 0) { if ((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror(lstat"); exit(1); } } unlink(fifo); if (mkfifo(fifo,0600) == -1) { perror(mkfifo"); exit(1); } Linux pipes are broken,we need O_RDWR instead of O_RDONLY */ #ifdef __linux socket = open(fifo,O_RDWR | O_NONBLOCK,128); line-height:1.5!important">0); #else socket = open(fifo,O_RDONLY | O_NONBLOCK,255); line-height:1.5!important">#endif if (socket == -open1); } fprintf(stderr,Write data to %s\n",fifo); #endif Initalize the event library */ event_init(); Initalize one event */ #ifdef WIN32 event_set(&evfifo,(evutil_socket_t)socket,EV_READ,fifo_read,&evfifo); #else event_set(&evfifo,socket,0); line-height:1.5!important"> Add it to the active events,without a timeout */ event_add(&evfifo,NULL); event_dispatch(); #ifdef WIN32 CloseHandle(socket); return (0); }
main函数里调用event_init()初始化一个event_base,
之后调用event_set对event设置了回调函数和读事件关注,
event_add将此事件加入event队列里,超时设置为空
最后调用event_dispatch()进行事件轮训派发。
fifo_read是一个回调函数,格式就是之前说的cb格式
1)头文主要就是 event.h:事件宏定义、接口函数声明,主要结构体 event 的声明;
2)内部头文件
xxx-internal.h:内部数据结构和函数,对外不可见,以达到信息隐藏的目的;
3) libevent 框架
event.c: event 整体框架的代码实现;
4)对系统 I/O 多路复用机制的封装
epoll.c:对 epoll 的封装;
select.c:对 select 的封装;
devpoll.c:对 dev/poll 的封装;
kqueue.c:对 kqueue 的封装;
5)定时事件管理
min-heap.h:其实就是一个以时间作为 key 的小根堆结构;
6)信号管理
signal.c:对信号事件的处理;
7)辅助功能函数
evutil.h 和 evutil.c:一些辅助功能函数,包括创建 socket pair 和一些时间操作函数:加、减
和比较等。
8)日志
log.h 和 log.c: log 日志函数
9)缓冲区管理
evbuffer.c 和 buffer.c: libevent 对缓冲区的封装;
10)基本数据结构
compat\sys 下的两个源文件: queue.h 是 libevent 基本数据结构的实现,包括链表,双向链表,
队列等; _libevent_time.h:一些用于时间操作的结构体定义、函数和宏定义;
11)实用网络库
http 和 evdns:是基于 libevent 实现的 http 服务器和异步 dns 查询库;
四:event结构知识
下面着重看下event结构体,这是libevent核心结构