创建一个event_base
在使用Libevent的接口前,你需要先创建一个或多个event_base数据结构。每个event_base持有一个sock集,可以轮询这个集合的sock来检查可以激活哪些事件。
如果在event_base中设置使用锁,那在多个线程中可以使用这个event_base。 但是event_base只能在单个线程中进行轮询。 如果你想在多个线程中为你的IO做轮询,那你需要为每个线程创建一个event_base。
提示: Libevent的未来版本中可能支持在多个线程中使用同个event_base进行轮询。
每个event_base有一个“方法”,或者说后端,用来检测哪个事件已经准备好了。 这个方法对应封装有(后文提到的“后端”,指代的是下面的一个或多个实现):
- select
- poll
- epoll
- kqueue
- devpoll
- evport
- win32
用户可以通过设置环境变量来禁用某些后端。 比如你想要禁用kqueue这个后端, 可以通过设置EVENT_NOKQUEUE
这个环境变量,其他的类似。如果你想在程序中禁用后端, 查看下面的event_config_avoid_method()
.
event_base默认设置
函数event_base_new()
创建并返回一个带有默认设置的event_base。 它先检查你的环境变量,然后返回一个指针,指向这个新创建的event_base。 如果出现错误,返回一个NULL指针。
对于上面提到的不同后端,它会选择一个当前系统支持的最高效率的后端来创建event_base。
struct event_base *event_base_new(void);
对于多数程序,创建一个默认设置的event_base已经足够了。 event_base_new()
定义在头文件<event2/event.h>
中,首次出现在Libevent 1.4.3.
event_base更多设置
如果你想对于创建哪种event_base有更多操作,那需要使用event_config。 event_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_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()
来忽略指定名称的后端。调用event_config_require_feature()
来设置Libevent不要使用不满足一些特性的那些后端。调用event_config_set_flag()
来告诉Libevent使用一个或多个运行时标识来创建event_base。
event_config_require_features
可使用的特性有:
- EV_FEATURE_ET: 要求后端能支持边沿触发IO(edge-triggered IO)
- EV_FEATURE_O1: 要求增加活删除一个事件,或者激活事件时,后端要满足O(1)的效率
- EV_FEATURE_FDS: 要求后端能支持任意类型的文件描述符,而不仅仅是socket
event_config_set_flag()
可使用的运行时标识有:
EVENT_BASE_FLAG_NOLOCK
设置event_base不要使用锁。使用这个标识可以在加锁和释放event_base时节省一些时间,但在多线程中使用同个event_base会变得不安全,也无法加锁其实。EVENT_BASE_FLAG_IGNOREENV
在选择后端时不要检查以`EVENT`开头的环境变量。谨慎使用这个标识,它将增加你调试代码和Libevent接口的难度。EVENT_BASE_FLAG_STARTUP_IOCP
只限Windows上使用,让Libevent运行后自动在需要时使用IOCP分发逻辑。EVENT_BASE_FLAG_NO_CACHE_TIME
设置这个标识,则每次轮询超时之后检查当前时间,而不是每次在每次轮询超时时做检查。这将使用更多cpu,使用时注意一下。EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST
告诉Libevent,如果它决定使用epoll这个后端,那它可以安全使用这类基于”changelist”的后端。在后端的分发函数被调用期间,若同个fd的状态被多次修改,那这个epoll-changelist后端能够避免一些不必要系统调用;但它也触发一个内核的bug:如果传一个通过dup()
或类似的函数来clone的fd给到Libevent,将导致错误的结果。如果你不是使用epoll这个后端,那这个标识没有效果。你也可以通过设置EVENT_EPOLL_USE_CHANGELIST
这个环境变量来打开epoll-changelist选项。EVENT_BASE_FLAG_PRECISE_TIMER
Libevent默认尝试使用系统提供的快速分时机制。如果有一个慢一些的但能提供更为精细度的分时机制,那通过这个标识来告诉Libevent使用这个慢一些的。如果系统不支持”慢一些但更精细”的机制,则这个标识没有效果。
上面设置event_config的函数,成功则返回0,失败返回-1。
注意
如果你通过event_config设置一个系统不支持的后端,那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。
int event_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, int max_callbacks,
int min_priority);
这个函数通过在检查高优先级事件之前,对低优先级回调函数可以被调用的次数进行限制,来防止优先级的逆转。如果最大时间间隔为空,则事件轮询会在每个回调之后检查时间;如果最大时间间隔不为空,则会在这个时间间隔之后重新扫描高优先级的事件。如果最多回调次数不为负数,那达到这个次数之后,事件继续轮询更多事件。这些规则适用于最小优先级或更高优先级的任何事件。
Example: Preferring edge-triggered backends
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. */
}
Example: Avoiding priority-inversion
struct event_config *cfg;
struct event_base *base;
cfg = event_config_new();
if (!cfg)
/* Handle error */;
/* I'm going to have events running at two priorities. I expect that
some of my priority-1 events are going to have pretty slow callbacks,
so I don't want more than 100 msec to elapse (or 5 callbacks) before
checking for priority-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_FLAG_IGNORE_ENV
首先出现在Libevent 2.0.2-alpha。
标识EVENT_BASE_FLAG_PRECISE_TIMER
首次出现在Libevent 2.1.2-alpha。
函数event_config_set_num_cpus_hint()
是在Libevent 2.0.7-rc才有,以及函数event_config_set_max_dispatch_interval()
首先出现在2.1.1-alpha。
其他的则都是首次出现在Libevent 2.0.1-alpha。
检查event_base的后端函数
有时你想查看event_base有哪些特性,或者event_base使用了哪个后端函数。
const char **event_get_supported_methods(void);
函数返回一个数组指针,数组包含当前Libevent支持的后端函数。数组最后一个元素是NULL指针。
Example
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]);
}
注意: 这个函数返回的是Libevent编译中支持的后端函数列表,
Inteface
const char *event_base_get_method(const struct event_base *base);
enum event_method_feature event_base_get_features(const struct event_base *base);
event_base_get_method()
返回event_base实际使用的后端函数。The event_base_get_features()
返回一个位掩码,标识event_base支持的特性。
Example
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("");
}
释放event_base
event_base不再使用时,你可以释放它.
void event_base_free(struct event_base *base);
注意这个函数并不会同时释放当前关联在这个event_base上的事件,不会关闭任何socket。
设置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。
新创建的事件的优先级则是从最重要的0到不重要的-1.
有一个常量EVENT_MAX_PRIORITIES
, 是参数n_priorities的最大值,n_priorities不能超过这个值。
注意: 你必须在激活任何事件之前调用这个函数,最好是在event_base创建之后马上调用。
int event_base_get_npriorities(struct event_base *base);
返回event_base的优先等级。例如:如果返回3,则有效的优先顺序范围为0,1,2.
新创建的事件关联到这个event_base后,事件的默认优先级初始化为n_priorities / 2
.
fock()之后重新初始化event_base
在fork()之后并不是所有的事件后端都能保持干净,所以,如果你的程序使用fork()或者相关联的系统调用来开始一个新的进程,而且你想继续使用之前的event_base,那你需要重新初始化event_base。
int event_reinit(struct event_base *base);
成功返回0,失败返回-1。
Example
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); /*...*/
}
不再使用event_base这个函数
以前使用的是struct event_base *event_init(void);
解释略。
– EOF –