在使用Libevent函数之前,需要分配一个或多个event_base结构。每一个event_base都持有一个events的集合,并且可以检测那些events是激活的。
如果设置event_base可以使用锁,那event_base是可以安全的在多线程环境中使用,但是,只能在单个的线程中进行event_base的loop。如果希望在多线程中进行IO轮询,那就需要每个线程都有一个event_base。(未来版本的Libevent可能会支持多线程支持event_base的loop)
每一个event_base都会有一个“后台方法”,用来检测哪一个events准备好了。主要包含下列方法:select、poll、epoll、kqueue、devpoll、evport、win32.
用户可以通过环境变量禁止某些特定的后台方法。比如,如果想要禁止kqueue后台,可以设置环境变量EVENT_NOKQUEUE。如果想在程序中关闭后台,可以参见下面的event_config_avoid_method()函数。
一:设置默认event_base
event_base_new()函数分配并返回一个默认设置属性的event_base。它检查环境变量,并返回一个指向新的event_base的指针。如果发生错误,则返回NULL。
struct event_base *event_base_new(void);
它会挑选操作系统支持的最快的后台方法。对于大多数程序来说,使用这个接口基本上就足够了。该接口在<event2/event.h>中声明。
二:设置复杂event_base
如果希望对event_base有更多的控制,那就需要event_config结构。event_config是一个不透明的结构,包含可以设置的event_base信息。可以通过传递event_config给函数event_base_new_with_config()对event_base进行设置。
struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);
使用这些函数分配event_base,可以通过event_config_new来分配新的event_config结构。然后调用其他函数,根据需要设置event_config。最后,调用event_base_new_with_config返回一个新的event_base。完成之后,使用event_config_free释放event_config结构。
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,
};
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
};
int event_config_set_flag(struct event_config *cfg,
enum event_base_config_flag flag);
调用event_config_avoid_method(),可以让Libevent避免使用特定后端方法。调用event_config_require_feature(),禁止Libevent使用任何无法提供一组特性的后端方法。调用event_config_set_flag(),可以在创建event_base时,设置一些运行时标志。
event_config_require_features支持的特性有:
EV_FEATURE_ET:需要支持边沿触发的后端方法
EV_FEATURE_O1:需要这样的后端方法,它的添加、删除event,或者确定哪个event变为激活等操作,都是O(1)的。
EV_FEATURE_FDS:需要可以支持任意文件描述符类型的后台方法,而不仅仅支持socket。
event_config_set_flag()支持的选项有:
EVENT_BASE_FLAG_NOLOCK:不为event_base分配锁。尽管这样做可以节省一点在event_base上加锁和解锁的时间,但是这会导致多线程环境下,无法安全的使用event_base。
EVENT_BASE_FLAG_IGNORE_ENV:在挑选使用哪个后台方法时,不检查EVENT_*环境变量,使用该标志之前要仔细考虑,因为他使得调试应用程序和Libevent之间的交互变得困难。
EVENT_BASE_FLAG_STARTUP_IOCP:仅用于Windows,该标志使得Libevent在启动时就使能必要的IOCP调度逻辑,而不是按需使能。
EVENT_BASE_FLAG_NO_CACHE_TIME:不是每次event loop准备运行超时回调函数时,检测当前时间,而是每次调用超时回调函数之后在检测。这样做会消耗额外的CPU,所以要小心。
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告诉Libevent,如果使用的是epoll后端方法,那可以安全的使用基于changelist的更快的后端方法。如果在调用后端调度函数的中间,同样的描述符的状态多次改变的话,epoll-changelist可以避免不必要的系统调用。但是如果传递给Libevent的描述符fd是dup复制而来的话,那么会触发一个内核bug。该标志只有在使用epoll后端时有效。设置EVENT_EPOLL_USE_CHANGELIST环境变量,同样可以开启epoll-changelist选项。
EVENT_BASE_FLAG_PRECISE_TIMER:默认情况下,Libevent使用系统提供的最快的定时机制,如果存在较慢的定时机制,但是它可以提供更精细的定时精度,那么该标志可以使Libevent使用这种定时机制。如果操作系统没有这样的定时机制的话,则该标志是无效的。
上述操作event_config的函数在成功是返回0,失败是返回-1.
注意:设置event_config请求操作系统没有提供的后台方法是很容易的。比如,对于Libevent2.0.1-alpha来说,Windows没有O(1)的后端方法,而且Linux也不能提供EV_FEATURE_FDS和 EV_FEATURE_O1的后端方法。如果配置的Libevent无法满足的话,那么event_base_new_with_config()会返回NULL。
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
该接口目前仅在Windows上使用IOCP时才支持,未来版本其他平台上也许也会支持。调用该接口,使得由event_config配置生成的event_base可以在多线程时更加有效的使用给定的cpu个数。注意,注意这仅仅是一个提示:event_base使用的CPU可能比你选择的要少。
intevent_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, intmax_callbacks,
int min_priority);
该接口可以防止优先级反转。这是通过在检查更高优先级的events之前,限制有多少低优先级的event回调可以被调用而实现的。如果max_interval非NULL,在每次回调之后,event loop都会检查时间,而且经过max_interval时间之后,重新扫描更高优先级的events。如果max_callbacks非0,那么max_callback个回调被调用之后,event loop会检查更多的events。这些规则针对min_priority以及以上的events有效。
该接口设置在经过多长时间,或者(以及)经过多少个回调之后,重新检查新的events。默认情况下,event_base在检查新events之前,会运行尽可能多的被激活的高优先级events。如果设置了max_interval之后,那么每次回调之后都会检查时间,保证检查新events的最长时间间隔就是max_interval。如果设置了大于0的max_callbacks,那么在检查新的events之前,最多运行max_callbacks个回调。
该接口可以减少高优先级events的延迟。而且在有多个低优先级events时,避免出现优先级倒置。但是这是以降低吞吐量为代价的。
max_interval :经过该时间之后,Libevent应该停止运行回调,而检查新的events,如果该参数为NULL,则无此时间限制。
max_callbacks:经过max_callbacks个回调之后,Libevent应该停止运行回调,并且检查更多的events。如果该参数为-1,则无此个数限制。
min_priority:低于该优先级的events,max_interval和max_callbacks将不会生效。如果该值为0,则所有优先级的events都会生效。如果置为1,则针对优先级为1,以及更高优先级的events才会生效。
struct event_config *cfg;
struct event_base *base;
int i;
/* My program wants to use edge-triggered events if atall possible. So
I'll try to geta base twice: Once insisting on edge-triggered IO, and
once not. */
for (i=0; i<2; ++i) {
cfg =event_config_new();
/* I don't likeselect. */
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 gethere, event_base_new_with_config() returned NULL. If
this is thefirst time around the loop, we'll try again without
settingEV_FEATURE_ET. If this is the secondtime around the
loop, we'llgive up. */
}
struct event_config *cfg;
struct event_base *base;
cfg = event_config_new();
if (!cfg)
/* Handle error*/;
/* I'm going to have events running at twopriorities. I expect that
some of mypriority-1 events are going to have pretty slow callbacks,
so I don't wantmore than 100 msec to elapse (or 5 callbacks) before
checking forpriority-0 events. */
struct timeval msec_100 = { 0, 100*1000 };
event_config_set_max_dispatch_interval(cfg,&msec_100, 5, 1);
base = event_base_new_with_config(cfg);
if (!base)
/* Handle error*/;
event_base_priority_init(base, 2);
以上所有的函数和类型都在<event2/event.h>中声明。
三:检测event_base的后端方法
有时你可能需要知道一个event_base实际具有哪些特性,或者使用哪一个后端方法。
const char **event_get_supported_methods(void);
event_get_supported_methods()返回一个指向数组的指针。该数组包含所有当前Libevent所支持的后端方法的名字,该数组最后一个元素为NULL。
int i;
const char **methods =event_get_supported_methods();
printf("Starting Libevent%s. Available methods are: ",
event_get_version());
for (i=0; methods[i] != NULL;++i) {
printf(" %s ", methods[i]);
}
注意:该函数返回的,是Libevent编译时支持的一系列后端方法,有可能Libevent在尝试运行时,操作系统实际上并不支持所有的方法。比如,如果你使用OSX系统的话,那么kqueue可能会有太多的漏洞而无法使用。
const char *event_base_get_method(conststruct event_base *base);
enum event_method_featureevent_base_get_features(const struct event_base *base);
event_base_get_method()方法返回的是event_base实际使用的后端方法名称。event_base_get_features()返回它所支持的特性的掩码组合。
struct event_base *base;
enum event_method_feature f;
base = event_base_new();
if (!base) {
puts("Couldn't get anevent_base!");
} else {
printf("Using Libevent with backendmethod %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("");
}
这些方法定义在文件<event2/event.h>中。
四:释放event_base
当一个event_base完成任务的时候,就可以调用event_base_free释放它。
void event_base_free(structevent_base *base);
注意,该函数并不释放任何与event_base所关联的events,也不会关闭sockets,更不会释放他们的指针。该方法在<event2/event.h>中定义。
五:设置event_base 的优先级
Libevent支持在event上设置多个优先级。默认情况下,一个event_base仅有一个优先级。可以通过函数event_base_priority_init来设置event_base的优先级等级数。
int event_base_priority_init(struct event_base*base, int n_priorities);
该函数成功时返回0,失败是返回-1。参数base是需要修改的event_base, n_priorities是支持的优先级等级数。该参数至少为1. 调用该函数之后,新的events的优先级等级就是从0(最高)到n_priorities -1(最低)。
常数EVENT_MAX_PRIORITIE,是n_priorities的值的上限。如果使用比该常数还要大的数调用该函数,就会发生错误。
注意,必须在任何events变为激活时调用该函数。最好是创建event_base之后就立即调用它。
调用event_base_getnpriorities(),可以得到当前event_base所支持的优先级等级个数。
int event_base_get_npriorities(structevent_base *base);
该方法返回在event_base上配置的优先级等级数。所以如果该函数返回3,那么支持的优先级就是0,1和2.
默认情况下,所有关联到event_base的events的初始优先级都是n_priorities/ 2。
函数event_base_priority_init,在<event2/event.h>中定义。
六:fork之后,重新初始化event_base
并不是所有event后端,在fork之后都能正确工作。所以,如果你的程序使用fork(或其他系统调用)开始一个新进程,如果在fork之后,还想继续使用event_base,那么需要对其进行重新初始化。
int event_reinit(structevent_base *base);
该方法在成功是返回0,失败是返回-1.
struct event_base *base =event_base_new();
/* ... add some events to theevent_base ... */
if (fork()) {
/* In parent */
continue_running_parent(base); /*...*/
} else {
/* In child */
event_reinit(base);
continue_running_child(base); /*...*/
}
该方法在<event2/event.h>中定义。
七:过时的event_base方法
老版本的Libevent严重依赖于“当前”event_base的概念。“当前”event_base是指所有线程都共享的全局设置。如果你忘了指定需要哪一个event_base,那会得到“当前”的那一个。因为event_base不是线程安全的,所以这样很容易出错。
event_base_new的老版本是:
struct event_base*event_init(void);
该方法类似于event_base_new,将当前event_base设置为分配的event_base。没有任何其他方法可以改变当前base。
本文描述的函数有一些用于操作当前event_base的变体,这些函数与新版本函数的行为类似,只是它们没有event_base参数。
Current function |
Obsolete current-base version |
event_base_priority_init() |
event_priority_init() |
event_base_get_method() |
event_get_method() |
http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html