Libevent创建event_base
Toc
  1. 创建一个event_base
  2. event_base默认设置
  3. event_base更多设置
    1. Example: Preferring edge-triggered backends
    2. Example: Avoiding priority-inversion
  4. 检查event_base的后端函数
    1. Example
  5. Inteface
    1. Example
  • 释放event_base
  • 设置event_base优先级
  • fock()之后重新初始化event_base
    1. Example
  • 不再使用event_base这个函数
  • 原文:R2: Getting an event_base

    创建一个event_base

    在使用Libevent的接口前,你需要先创建一个或多个event_base数据结构。每个event_base持有一个sock集,可以轮询这个集合的sock来检查可以激活哪些事件。

    如果在event_base中设置使用锁,那在多个线程中可以使用这个event_base。 但是event_base只能在单个线程中进行轮询。 如果你想在多个线程中为你的IO做轮询,那你需要为每个线程创建一个event_base。

    提示: Libevent的未来版本中可能支持在多个线程中使用同个event_base进行轮询。

    每个event_base有一个“方法”,或者说后端,用来检测哪个事件已经准备好了。 这个方法对应封装有(后文提到的“后端”,指代的是下面的一个或多个实现):

    1. select
    2. poll
    3. epoll
    4. kqueue
    5. devpoll
    6. evport
    7. 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可使用的特性有:

    1. EV_FEATURE_ET: 要求后端能支持边沿触发IO(edge-triggered IO)
    2. EV_FEATURE_O1: 要求增加活删除一个事件,或者激活事件时,后端要满足O(1)的效率
    3. EV_FEATURE_FDS: 要求后端能支持任意类型的文件描述符,而不仅仅是socket

    event_config_set_flag()可使用的运行时标识有:

    1. EVENT_BASE_FLAG_NOLOCK
      设置event_base不要使用锁。使用这个标识可以在加锁和释放event_base时节省一些时间,但在多线程中使用同个event_base会变得不安全,也无法加锁其实。

    2. EVENT_BASE_FLAG_IGNOREENV
      在选择后端时不要检查以`EVENT
      `开头的环境变量。谨慎使用这个标识,它将增加你调试代码和Libevent接口的难度。

    3. EVENT_BASE_FLAG_STARTUP_IOCP
      只限Windows上使用,让Libevent运行后自动在需要时使用IOCP分发逻辑。

    4. EVENT_BASE_FLAG_NO_CACHE_TIME
      设置这个标识,则每次轮询超时之后检查当前时间,而不是每次在每次轮询超时时做检查。这将使用更多cpu,使用时注意一下。

    5. EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST
      告诉Libevent,如果它决定使用epoll这个后端,那它可以安全使用这类基于”changelist”的后端。在后端的分发函数被调用期间,若同个fd的状态被多次修改,那这个epoll-changelist后端能够避免一些不必要系统调用;但它也触发一个内核的bug:如果传一个通过dup()或类似的函数来clone的fd给到Libevent,将导致错误的结果。如果你不是使用epoll这个后端,那这个标识没有效果。你也可以通过设置EVENT_EPOLL_USE_CHANGELIST这个环境变量来打开epoll-changelist选项。

    6. 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 –

    Categories: in_lib
    Tags: libevent