Libevent:3创建event_base

         在使用Libevent函数之前,需要分配一个或多个event_base结构。每一个event_base都持有一个events的集合,并且可以检测那些events是激活的。

         如果设置event_base可以使用锁,那event_base是可以安全的在多线程环境中使用,但是,只能在单个的线程中进行event_baseloop。如果希望在多线程中进行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。如果设置了大于0max_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:\n",

    event_get_version());

for (i=0; methods[i] != NULL;++i) {

    printf("    %s\n", 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

posted @ 2015-01-18 19:11  gqtc  阅读(507)  评论(0编辑  收藏  举报