【简介】
libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、
kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使
用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
【基础函数】
1. event_init() event_base_new()
绍。
2. event_dispatch() event_base_dispatch()
接管事件. event_base_dispatch()配合event_base_new使用,线程安全。
3. event_set() event_base_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
参数指定. 初始化event结构体变量中的回调函数是程序调用者必须提供的.
参数:
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_base_new要使用event_base_set。
int event_base_set
Associate a different event base with an event.
Parameters:
4. event_add()
event_add()函数使通过event_set()设置的事件在事件匹配或超时时(如果设置了超时)被执行.
event结构体变量必须先用event_set()初始化过,并且在事件被删除前不得再次调用event_set()来初始化
之.如果事件发生超时,旧的超时时间会被新的超时时间所取代.
5. event_del()
event_del()函数会取消event结构体所指定的事件,如果该事件已经执行或没有注册(在事件链表中不存
在),该函数不会产生任何作用.
6. 时间定时器系列
evtimer_set(), evtimer_add(), evtimer_del(),evtimer_initialized() evtimer_pending()等函数
()用于设置定时或超时操作.在这些函数中,文件描述符为-1,事件类型为EV_TIMEOUT.
7. 信号系列
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.
8. event_pending
event_pending
event_pending()用于检测event结构体变量指定的事件是否处于等待状态.如果设定了EV_TIMEOUT,并
且tv结构体指针变量非空,则事件终止时间由tv返回.
9.event_initialized
【示例代码】
---------------------------------开始---------------------------------------
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#endif
#include "event-config.h"
#ifdef _EVENT___func__
#define __func__ _EVENT___func__
#endif //
#if 0
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/queue.h>
#include <signal.h>
#include <errno.h>
#endif //if 0
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#ifndef WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <event2/event.h>
#include "event2/event_struct.h"
#include <event2/event_compat.h>
#include "event2/tag.h"
#include <event2/dns.h>
#include <event2/dns_compat.h>
#include <event2/bufferevent.h>
#include "event2/bufferevent_struct.h"
#include <event2/buffer.h>
#include "event2/buffer_compat.h"
#include <event2/thread.h>
#include "event-internal.h"
#include <event2/util.h>
#include "log-internal.h"
//#include "event-config.h"
#include <iocp-internal.h>
/关键代码段
#ifdef WIN32
#define write(fd,buf,len) send((fd),(buf),(len),0)
#define read(fd,buf,len) recv((fd),(buf),(len),0)
#endif
// .;"../libevent/include/WIN32-Code";../libevent/include/compat;../libevent/include
#ifdef WIN32
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "../libevent/lib/libevent.lib")
#endif //WIN32
static void simple_read_cb(evutil_socket_t fd, short event, void *arg);
static int cleanup_test(void);
//int pair[2];
#define TEST1_STR "test 1 string"
typedef struct basic_test_data {
}basic_test_data;
struct basic_test_data *g_data = NULL;
int g_spair[2] = { -1, -1 };
static void *basic_test_setup()
{
}
static int cleanup_test(void) {
#ifndef WIN32
#else
#endif
}
int test1_main(void *ptr)
{
}
int main(int argc, char **argv)
{
}
---------------------------------结束---------------------------------------
【evbuffer简介】
libevent的缓冲是一个连续的内存区域,其处理数据的方式(写数据和读数据)更像一个
队列操作方式:从后写入,从前读出。evbuffer分别设置相关指针(一个指标)用于指示
读出位置和写入位置。其大致结构如图:
orig_buffer指向由realloc分配的连续内存区域,buffer指向有效数据的内存区域,totallen表示orig_buffer指向的内存区域的大小,misalign表示buffer相对于orig_buffer的偏移,off表示有效数据的长度。
【evbuffer相关函数】
X. evbuffer_new
创建缓冲evbuffer
X. evbuffer_read
从文件描述符(socket句柄)中读取数据到evbuffer数据结构中
int evbuffer_read
Read from a file descriptor and store the result in an evbuffer.
Parameters:
Returns:
the number of bytes read, or -1 if an error occurred
X. evbuffer_write
evbuffer数据结构中数据写到文件描述符(socket句柄)中。
int evbuffer_write
Write the contents of an evbuffer to a file descriptor.
The evbuffer will be drained after the bytes have been successfully written.
Parameters:
Returns:
the number of bytes written, or -1 if an error occurred
X. evbuffer_drain:
buffer指针后移,同时增大misalign,减小off,而该函数正是做这件事的。说白了,
该函数就是用于调整缓冲队列的前向指标。
void
Parameters:
X. evbuffer_remove:
然后调用evbuffer_drain移动相关指标。
int
Read data from an event buffer and drain the bytes read.
Parameters:
Returns:
the number of bytes read
X. evbuffer_add:
闲内存,如果没有则调用evbuffer_expand扩充之,然后直接memcpy,更新off指标。
int
Append data to the end of an evbuffer.
Parameters:
X. evbuffer_expand:
buffer+off后,buffer到buffer+off之间已被使用,保存的是有效数据,而
orig_buffer和buffer之间则是因为读取数据移动指标而形成的无效区域。
evbuffer_expand的扩充策略在于,首先判断如果让出orig_buffer和buffer之间的空闲
区域是否可以容纳添加的数据,如果可以,则移动buffer和buffer+off之间的数据到
orig_buffer和orig_buffer+off之间(有可能发生内存重叠,所以这里移动调用的是
memmove),然后把新的数据拷贝到orig_buffer+off之后;如果不可以容纳,那么重新
分配更大的空间(realloc),同样会移动数据。扩充内存的策略为:确保新的内存区域
最小尺寸为256,且以乘以2的方式逐步扩大(256、512、1024、...)。
【示例代码】
主要函数代码,其它代码参考上篇文章
---------------------------------开始---------------------------------------
int test3_main(void *ptr)
{
}
static void
simple_evbuffer_read_cb(evutil_socket_t fd, short event, void *arg)
{
}
---------------------------------结束---------------------------------------
【其它方面】
1. libevent中的epoll用的是水平触发,这种效率还是没有边缘触发高,直接使用
epoll还能带来性能上的提升可能性。
2. 线程安全问题
libevent如果用 event_init初始化,将不支持多线程,或者说它是多线程不安全的,
因此也受到限制。
解决的方案一:
参考文章:http://www.cppblog.com/converse/archive/2009/01/12/71809.html
http://it.newnew.cn/it53180.aspx
解决方案二:使用event_base_new()进行初始化,并使用互斥锁。
【参考】
X. libevent主页
http://monkey.org/~provos/libevent/
X. 官方文档
http://monkey.org/~provos/libevent/doxygen-2.0.1/index.html
X. 源码分析-libevent事件处理框架分析
http://www.cppblog.com/converse/archive/2009/01/03/71040.html
X. socketpair相关
http://liulixiaoyao.blog.51cto.com/1361095/533469
X. 教程
libevent网络编程
http://www.wangafu.net/~nickm/libevent-book/TOC.html
libevent网络编程 中文翻译
http://blog.sina.com.cn/s/articlelist_1457448730_0_1.html
http://blog.sina.com.cn/s/blog_56dee71a0100q2i9.html
http://blog.sina.com.cn/s/blog_56dee71a0100qx4s.html
其它
http://hustlg.bokee.com/6573083.html
http://blog.csdn.net/dlmu2001/archive/2010/06/20/5682456.aspx
http://monkey.org/~provos/libevent/doxygen-1.4.10/event_8h.html
X. echo server
http://ishbits.googlecode.com/svn-
history/r5/trunk/libevent_echosrv/libevent_echosrv1.c
X. evbuffer使用
http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html
http://team.eyou.com/?p=32
X. spserver相关
http://code.google.com/p/spserver/
http://iunknown.javaeye.com/blog/59804
----------------------------------------------------------------------
本文原创,转载请注明出处 http://blog.sina.com.cn/faithfish