使用Libevent库函数进行编程时,我们一般需要先分配一个或多个event_base结构体。每一个event_base都包含了一组事件,并且通过轮询确定哪些事件是激活的。
每一个event_base都有一个“方法”(或称 “后端”(backend)),用来确定哪些事件是激活了的。这些“方法”主要有:select, poll, epoll, kqueue, devpoll, evport, win32等。用户可以使用环境变量来禁止某个或某些特定的后端方法。比如,通过设置EVENT_NOKQUEUE环境变量来关闭kqueue后端方法。
1.event_base的创建
(1)event_base_new()
1 2 3 4 5 |
头文件:<event2/event.h> 原型:struct event_base *event_base_new(void); 功能:创建一个新的event_base对象 参数:空 返回值:成功,返回一个event_base指针;失败,返回NULL。 |
注:此方法创建的是一个拥有默认配置的event_base对象,并且它会根据你的操作系统从可用的“后端方法”中选择一个最高效的方法。
(2)event_base_new_with_config()
event_base_new()创建的event_base对象使用的是默认的设置,如果你想要在创建event_base对象的时候就指定一些配置的话,需要使用下面这个函数:
1 2 3 4 5 |
头文件:<event2/event.h> 原型:struct event_base *event_base_new_with_config(const struct event_config *cfg); 功能:创建一个特定配置的event_base对象 参数:cfg,用来配置event_base对象 返回值:成功,返回一个event_base指针;失败,返回NULL。 |
其中,参数cfg对象的创建和销毁使用如下函数:
1 2 |
struct event_config *event_config_new(void); void event_config_free(struct event_config *cfg); |
通过event_config_new()函数创建了event_config对象之后,就可以做一些配置操作了。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/*不使用method指定的后端方法*/ int event_config_avoid_method(struct event_config *cfg, const char *method); enum event_method_feature { EV_FEATURE_ET = 0x01, EV_FEATURE_O1 = 0x02, EV_FEATURE_FDS = 0x04, }; /*设置后端方法的特性。比如,边缘触发、O(1)时间复杂度等*/ int event_config_require_features(struct event_config *cfg, enum event_method_feature feature); enum event_base_config_flag { EVENT_BASE_FLAG_NOLOCK = 0x01, EVENT_BASE_FLAG_IGNORE_ENV = 0x02, EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 }; /*为event_base设置一个或多个标志位*/ int event_config_set_flag(struct event_config *cfg, enum event_base_config_flag flag); |
其中,event_method_feature包含以下字段:
1 2 3 |
EV_FEATURE_ET:需要一个支持边缘触发的后端方法IO; EV_FEATURE_O1:需要一个添加或删除单个事件以及使单个事件变成活跃状态是O(1)时间复杂度的后端方法; EV_FEATURE_FDS:需要一个不仅仅支持套接字,而是支持任意文件描述符类型的后端方法; |
event_base_config_flag包含以下字段:
1 2 3 4 5 6 |
EVENT_BASE_FLAG_NOLOCK:不为event_base分配锁; EVENT_BASE_FLAG_IGNORE_ENV:在选择使用哪一个后端方法时,不要检查EVENT_*环境变量; EVENT_BASE_FLAG_STARTUP_IOCP:windows系统的选项; EVENT_BASE_FLAG_NO_CACHE_TIME:当准备运行超时回调函数时,不检查当前时间,而是在调用超时回调函数之后检查; EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告诉libevent,如果决定使用epoll后端方法,那么使用epoll-changelist是安全的; EVENT_BASE_FLAG_PRECISE_TIMER:告诉libevnet,使用精度更高的定时机制(如果有的话)。 |
对此,有如下的一般使用方法:
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 |
struct event_config *cfg; struct event_base *base; int i; /* My program wants to use edge-triggered events if at all possible. So I'll try to get a base twice: Once insisting on edge-triggered IO, and once not. */ for (i=0; i<2; ++i) { cfg = event_config_new(); /* I don't like select. */ event_config_avoid_method(cfg, "select"); if (i == 0) event_config_require_features(cfg, EV_FEATURE_ET); base = event_base_new_with_config(cfg); event_config_free(cfg); if (base) break; /* If we get here, event_base_new_with_config() returned NULL. If this is the first time around the loop, we'll try again without setting EV_FEATURE_ET. If this is the second time around the loop, we'll give up. */ } |
2.event_base的销毁
使用完event_base对象之后,使用如下函数进行资源释放:
1 2 3 4 5 |
头文件:<event2/event.h> 原型:void event_base_free(struct event_base *base) 功能:释放event_base对象 参数:base——待释放的event_base对象 返回值:空 |
需要注意的一点是:该函数只会释放event_base对象本身,并不会去释放当前与event_base关联的任何事件以及事件套接字等等。
3.检查event_base
(1)event_get_supported_methods()
获取Libevent支持的方法的名字数组:
1 2 3 4 5 |
头文件:<event2/event.h> 原型:const char **event_get_supported_methods(void); 功能:获取libevent支持的所有事件通知机制 参数:空 返回值:成功,返回一个指针,指向该版本的libevent支持的方法名称数组;失败,返回NULL |
比如,有如下使用方法:
1 2 3 4 5 6 7 |
int i; const char **methods = event_get_supported_methods(); printf("Starting Libevent %s. Available methods are:\n", event_get_version()); for (i=0; methods[i] != NULL; ++i) { printf(" %s\n", methods[i]); } |
(2)event_base_get_method()
获取event_base正在使用的后端方法:
1 2 3 4 5 |
头文件:<event2/event.h> 原型:const char *event_base_get_method(const struct event_base *base) 功能:获取Libevent使用的内核事件通知机制(如epoll, select…) 参数:base——event_base对象 返回值:返回base所使用事件通知机制 |
类似的函数还有event_base_get_features(),该函数返回event_base支持的特性的位掩码。一般使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct event_base *base; enum event_method_feature f; base = event_base_new(); if (!base) { puts("Couldn't get an event_base!"); } else { printf("Using Libevent with backend method %s.", event_base_get_method(base)); f = event_base_get_features(base); if ((f & EV_FEATURE_ET)) printf(" Edge-triggered events are supported."); if ((f & EV_FEATURE_O1)) printf(" O(1) event notification is supported."); if ((f & EV_FEATURE_FDS)) printf(" All FD types are supported."); puts(""); } |
4.event_base优先级
(1)设置优先级个数
Libevent在默认情况下只支持单个优先级。通过调用下面这个函数,可以设置event_base支持优先级的数量。
1 2 3 4 5 |
头文件:<event2/event.h> 原型:int event_base_priority_init(struct event_base *base, int n_priorities) 功能:设置event_base支持的优先级个数 参数:base——event_base对象;n_priorities——优先级个数,最小为1 返回值:成功,0;失败,-1 |
注:优先级范围为0到n_priorities-1。数字越小,优先级越高;数字越大,优先级越低。
(2)获取优先级个数
如果想要获取当前event_base支持的优先级个数,使用该函数:
1 |
int event_base_get_npriorities(struct event_base *base); |
5.event_base与fork
在调用fork()生成子进程后,如果你想要在子进程中继续使用event_base对象,你需要重新初始化它:
1 2 3 4 5 |
头文件:<event2/event.h> 原型:int event_reinit(struct event_base *base); 功能:重新初始化event_base对象 参数:base——event_base对象 返回值:成功,返回0;失败,返回-1 |
一般使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
struct event_base *base = event_base_new(); /* ... add some events to the event_base ... */ if (fork()) { /* In parent */ continue_running_parent(base); /*...*/ } else { /* In child */ event_reinit(base); continue_running_child(base); /*...*/ } |
6.其他
如果将一个event_base对象开启锁,那么在多线程中访问它将是安全的。但是需要注意的是,目前event_base的循环只能在单个线程中进行。如果你想要对IO进行多线程轮询的话,则需要为每一个线程创建一个event_base对象。
参考:
http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html