libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名的用于apache的php缓存库memcached据说也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
编译库代码,编译脚本会判断OS支持哪种类型的事件机制(select、epoll或kqueue),然后条件编译相应代码,供上层使用的接口仍然是保持统一的(否则也不能所谓的跨平台了)。在linux redhat as 4 u 2 上编译相当容易,configure以后make,make install就可以了,windows上编译似乎有点小麻烦,不过稍微改点东西也就通过了。
从代码中看,libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,在定时器的实现上使用了RB tree的数据结构,以达到高效查找、排序、删除定时器的目的,网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libevent的epoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。
跟网络无关的,libevent也有一些缓冲区管理的函数,而且是c风格的函数,实用性不是太大。libevent没有提供缓存的函数。
虽然libevent实用上的价值不大,但它提供的接口形式还是不错的,实现类似的lib的时候仍然是可以参考的。
Libevent定时器的数据结构自version 1.4起已由红黑树改为最小堆(Min Heap),以提高效率;网络IO和信号的数据结构采用了双向链表(TAILQ)。在实现上主要有3种链表: EVLIST_INSERTED, EVLIST_ACTIVE, EVLIST_TIMEOUT,一个ev在这3种链表之间被插入或删除,处于EVLIST_ACTIVE链表中的ev最后将会被调度执行。
Libevent提供了DNS,HTTP Server,RPC等组件,HTTP Server可以说是Libevent的经典应用。从http.c可看到Libevent的很多标准写法。写非阻塞式的HTTP Server很容易将socket处理与HTTP协议处理纠缠在一起,Libevent在这点上似乎也有值得推敲的地方。
libevent的功能:
这组事件API提供了一种当某个指定文件描述符有效或时间到达时执行某个函数的机制.
在使用事件API前必须使用event_init()初始化.
In order to process events, an application needs to call
为了能够处理事件,应用程序必须调用event_dispatch(),该函数只当错误时返回,这时应该由用应程序接管事件.
函数event_set()
(原型
event_set(struct event *ev, int fd, short event,
void (*fn)(int, short, void *), void *arg);
)
用于生成事件结构体ev,以备event_add()和event_del()使用.事件驱动程序将会调用void (*fn)(int, short, void *)中fn指定的函数,并传递三个参数int:文件描述符,short:事件类型,void*:其它参数由arg参数指定.
int fd 指定要监视的文件描述符,
short event 可以是EV_READ,EV_WRITE或EV_READ|EV_WRITE表示该文件可以无阻塞地进行读写.
fn函数将会被调用,并传递给三个变量:
int fd:触发事件的文件描述符.
short event:触发事件的类型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE.
void* :由arg参数指定的变量.
另外重复注册的事件将会产生重复的事件通知.EV_PERSIST可以让注册的事件在执行完后不被删除,直到调用event_del()删除.
结构体初始化完成后,在无需改变内容的情况下,可以被event_add(),event_del()重复使用.但是当结构体被event_add()添加之后,必须保持结构体的内容,直到事件被执行后退出或调用event_del()删除该事件.不允许将这个结构体变量注册完事件后重复使用.每一个描述符都需要一个单独的event结构体变量.
event_add()函数使通过event_set()设置的事件在事件匹配或超时时(如果设置了超时)被执行.
event结构体变量必须先用event_set()初始化过,并且在事件被删除前不得再次调用event_set()来初始化之.如果事件发生超时,旧的超时时间会被新的超时时间所取代.
event_del()函数会取消event结构体所指定的事件,如果该事件已经执行或没有注册(在事件链表中不存在),该函数不会产生任何作用.
evtimer_set(), evtimer_add(), evtimer_del(),evtimer_initialized() evtimer_pending()等函数()用于设置定时或超时操作.在这些函数中,文件描述符为-1,事件类型为EV_TIMEOUT.
signal_set(), signal_add(), signal_del(),signal_initialized(),signal_pending()等函数从略,其中事件类型为EV_SIGNAL.那就意味着signal_set() 添加了EV_PERSIST.
为了避免信号竞争,事件API提供了两程变量:event_sigcb 和 event_gotsig.某个信号的句柄设置event_gotsig表示收到信号.应用程序把event_sigcb设置成一个回调函数.当信号句柄设置了
event_gotsig之后,event_dispatch函数会执行回调函数处理接收到的信号.当没有事件注册时回调函数返回1.回调函数可以返回-1表示错误,这将导致event_dispatch()结束,错误代码为EINTR.
函数跟相似,但是它只调用回调函数一次,并且不需要调用者准备event结构体变量.该函数支持EV_TIMEOUT,EV_READ, andEV_WRITE.
event_pending()用于检测event结构体变量指定的事件是否处于等待状态.如果设定了EV_TIMEOUT,并且tv结构体指针变量非空,则事件终止时间由tv返回.
event_initialized()用于检测event结构体变量是否已经初始化.
event_loop提供一个接口用于单向执行等待事件.EVLOOP_ONCE和 EVLOOP_NONBLOCK有效.调用event_loopexit函数从事件循环中退出.在结定时间超时后下一个event_loop()重复将会正常完成然后不再等待事件直接退出.之后的event_loop()调用将会被正常执行.调用event_loopbreak函数直接从事件循环中退出. 下一个事件完成后event_loop()会中止退出.event_loopbreak()典型的是被事件回调函数调用,这个特性类似于执行了break;语句.之后的event_loop()调用会正常进生
初始化event结构体变量中的回调函数是程序调用者必须提供的.
事件优先级
默认情况下,libevent以相同的优先级调度法动的事件.但是有时候希望以较其它事件更高的优先级处理某些事件.正因为如此,libevent支持精确的优先级队列.优先级值较低活动事件总是比优先级值较高的活动事件.
不同的优先级别可以使用event_priority_init()函数来初始化.该函数必须在之前调用.event_priority_set()函数用于设置事件的优先级.默认情况下libevent把所有事件的优先级设置成中间值,除非它们的优先级被明确指定.
线程安全事件
Libevent支持线程安全,当初始化事件库时调用event_init(),返回一个事件根基event base.这个事件根基可以被event_base_set(),event_base_dispatch(), event_base_loop(), event_base_loopexit(),bufferevent_base_set() andevent_base_free().等函数共同使用.
event_base_set()应该在event_set()初始化之后调用,因为函数event_set()对最近创建的事件根基赋值.在调用bufferevent_new()初始化缓存事件之后应该调用bufferevent_base_set().当不再需要事件根基时应该调用函数释放内存.
缓存事件
libevent 提供正常事件回调的一个抽象.这个抽象叫缓存事件.缓存事件提供输入输出缓存自动写入和读出.使用缓存事件的程序员不再需要直接处理IO,而是通过读写输入输出缓存.
bufferevent_new()使用创建新的缓存事件.
(原型:
struct bufferevent *
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, everrorcb,
void *cbarg);
)
int fd:要读写数据的文件描述符.访文件描述符不允许是管道
接下来三个参数是回调:读写回调函数原型void (*cb)(struct bufferevent *bufev, void *arg)
错误处理回调函数原型:void (*cb)(struct bufferevent *bufev, short what, void *arg)
其中arg参数由cbarg指定.读写回调函数可以是NULL,错误处理回调函数必须指定.
一旦初始化,缓存事件结构体可以被bufferevent_enable(),bufferevent_disable()重复使用.标志参数可以是EV_READ和EV_WRITE.当文件可读时缓存试图从文件描述符中读取数据然后调用回调函数.当缓存中的数据少于写的最低"水位线"时,写回调函数被调用.该最低"水位线"默认为0.
bufferevent_write()用于往文件中写入数据.该数据被自动释放到输出缓冲区,当文件可写时写入文件描述符.该函数成功返回0,失败返回-1.
bufferevent_read()用于读取输入缓冲区中的数据,返回读取的字节数.
如果使用多个事件根基,bufferevent_base_set()函数必须在延缓一次使能缓存事件时调用.
无阻塞HTTP支持
libevent支持所有轻量级的HTTP层,可以且来作HTTP服务器也可以用来发HTTP请求.
HTTP服务器可以使用来创建calling evhttp_new().
也可以用evhttp_bind_socket()绑定所有端口和地址.当HTTP服务器不再使用时,可以调用evhttp_free()释放.
要收到HTTP请求,用户应该注册一个HTTP服务器回调,可以用 evhttp_set_cb()来实现.该函数的第二个参数是回调函数注册的URI.相应的回调会收到一个evhttp_request的结构体对象,它包含请求的所有信息.
这里不有将所有函数调用一一介绍,请参考event.h查看API接口.