创建event_base

在你使用libevent的函数之前,你应该先创建一个或多个event_base结构,每一个event_base拥有event集合,并且轮询它们看哪个处于active状态。

如果event_base设置为使用锁,那么在多线程中使用是安全的,但是只能在一个线程中循环,如果你想在多个线程中轮询IO,你需要为每个线程分配一个event_base。

以后的libevent版本可能会支持event_base在多个线程中轮询。

每个event_base都有一个后端方法,来决定哪个event已经准备好了,方法如下:

  • select
  • poll
  • epoll
  • kqueue
  • devpoll
  • evport
  • win32

使用者可以使用环境变量来禁用指定的方法,如果你想禁用kqueue方法,你可以设置EVENT_NOKQUEUE 环境变量。如果你想在你的程序中关闭某个方法,请参考下面的event_config_avoid_method方法。

默认的event_base

event_base_new函数检测环境变量,以默认的参数设置创建并返回了event_base的指针,返回NULL代表出错。

他总是在系统中选择一个最快的方法。

struct event_base *event_base_new(void);

对大多数程序而言,这已经足够了。

event_base_new()函数在<event2/event.h>中声明。它首次出现在Libevent 1.4.3中。

设置更复杂的event_base

如果你想对event_base做更多的控制,你可以使用event_config,event_config是一个对用户不透明的结构,里面保存着你的偏好信息,当你创建event_base的时候,你可以使用event_base_new_with_config函数传入一个event_config参数。

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方法告诉libevent在构造event_base的时候设置某些标志位。

event_config_require_features 支持的特性如下:

  • EV_FEATURE_ET

    支持边缘触发

  • EV_FEATURE_O1

    添加或删除单个事件,或使单个事件变为活动状态,是一个 O(1) 操作。

  • EV_FEATURE_FDS

    支持任意的文件描述符,不仅仅是socket

event_config_set_flag支持的选项如下:

  • EVENT_BASE_FLAG_NOLOCK

    不要为event_base分配锁,设置这个方法可能会在给event_base上锁和解锁的操作上节省一点时间,但是这样会造成在多线程之间访问不安全。

  • EVENT_BASE_FLAG_IGNORE_ENV

    在决定使用后端哪个方法的时候,不再检查以EVENT_开头的环境变量,在使用此选项之前请认真想好,因为这会让使用者难以调试你的程序。

  • EVENT_BASE_FLAG_STARTUP_IOCP

    仅在 Windows 上,此标志使 Libevent 在启动时启用任何必要的 IOCP 调度逻辑,而不是按需启用。

  • EVENT_BASE_FLAG_NO_CACHE_TIME

    不是每次事件循环准备运行超时回调时检查当前时间,而是在每次超时回调后检查它。这可能会使用比您预期更多的 CPU,所以要小心!

  • EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST

    告诉 Libevent,如果它决定使用 epoll 后端,使用更快的基于“changelist”的后端是安全的。 epoll-changelist 后端可以避免在同一个 fd 在调用后端的 dispatch 函数之间多次修改其状态的情况下不必要的系统调用,但它也会触发内核错误,如果你给 Libevent 克隆的任何 fd 会导致错误的结果dup() 或其变体。如果您使用 epoll 以外的后端,则此标志无效。您还可以通过设置 EVENT_EPOLL_USE_CHANGELIST 环境变量来打开 epoll-changelist 选项。

  • EVENT_BASE_FLAG_PRECISE_TIMER

    默认情况下,libevent使用操作系统提供的最快的计时机制,如果有一个较慢的计时机制可以提供更细粒度的控制,这个标志位告诉libevent使用该计时机制来代替,如果操作系统没有提供较慢但是更精确的计时机制,那么这个标志位将会被忽略。

上述操作event_config的函数在成功时返回0,失败时返回-1.

注意:

设置您的操作系统未提供的后端的event_config很容易。例如,从 Libevent 2.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。请注意,这只是一个建议:事件库最终可能会使用比您选择的更多或更少的 CPU。

int event_config_set_max_dispatch_interval(struct event_config *cfg,
    const struct timeval *max_interval, int max_callbacks,
    int min_priority);

此函数通过在检查更多高优先级事件之前限制可以调用的低优先级事件回调的数量来防止优先级倒置。如果 max_interval 为非空,则事件循环会在每次回调后检查时间,如果 max_interval 已过,则重新扫描高优先级事件。如果 max_callbacks 为非负,则事件循环还会在调用 max_callbacks 回调后检查更多事件。这些规则适用于 min_priority 或更高的任何事件。

例子:首选支持边缘触发的后端方法

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. */
}

例子:避免优先权倒置

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的后端方法

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:\n",
    event_get_version());
for (i=0; methods[i] != NULL; ++i) {
    printf("    %s\n", methods[i]);
}

注意:

这个函数返回libevent所支持的方法列表,但是你的操作系统可能并不支持,比如,因为OSX上的kqueue方法有太多的bug,所以你不应该使用此方法。
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使用的方法,event_base_get_features方法返回一个位掩码代表该方法支持的特性。

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("");
}

这些函数在<event2/event.h>中定义。

释放event_base

当你结束使用event_base的时候,你可以调用event_base_free来释放此结构。

void event_base_free(struct event_base *base);

此函数并不释放与此event_base关联的任何event,也不关闭sockets,也不释放任何指针。

设置event_base的优先级

Libevent 支持在一个事件上设置多个优先级。但是,默认情况下,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,新的event的优先级的取值为0(最高)~n_priorities(最低)

注意,你必须在任何时间变为active之前调用此方法,最好在创建event_base之后立马调用。

要获得当前event_base支持的优先级数量,可以调用如下方法:

int event_base_get_npriorities(struct event_base *base);

返回值为event_base所支持的优先级数量,如果该函数返回3,那么所允许的数字为0、1和2.

默认情况下,所有与event_base关联的新的event,默认优先级为n_priorities / 2

event_base_priority_init函数在<event2/event.h>中定义。

在fork之后重新初始化event_base

并不是所有的后端方法在调用了fork()函数之后仍然有效,所以你需要在调用了fork()或者相关的系统调用之后重新初始化。

int event_reinit(struct event_base *base);

成功时返回0,失败时返回-1.

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_reinit()函数在<event2/event.h>中定义。

标签: libevent

添加新评论