前面的博文都是讲一些Libevent的一些辅助结构,如今来说一下关键结构体:event_base。html
这里做一个提醒,在阅读Libevent源码时,会常常看到backend这个单词。其直译是“后端”。实际上其指的是Libevent内部使用的多路IO复用函数,多路IO复用函数就是select、poll、epoll这类函数。本系列博文中,为了叙述方便,“多路IO复用函数”与“后端”这两种说法都会采用。linux
一般咱们获取event_base都是经过event_base_new()这个无参函数。使用这个无参函数,只能获得一个默认配置的event_base结构体。本文主要是讲一些怎么获取一个非默认配置的event_base以及能够对event_base进行哪些配置。后端
仍是先看一下event_base_new函数吧。安全
//event.c文件 struct event_base * event_base_new(void) { struct event_base *base = NULL; struct event_config *cfg = event_config_new(); if (cfg) { base = event_base_new_with_config(cfg); event_config_free(cfg); } return base; }
能够看到,其先建立了一个event_config结构体,并用cfg指针指向之,而后再用这个变量做为参数调用event_base_new_with_config。由于并无对cfg进行任何的设置,因此获得的是默认配置的event_base。socket
从这里也能够知道,若是要对event_base进行配置,那么对cfg变量进行配置便可。如今咱们的目光从event_base结构体转到event_config结构体。ide
先来看看event_config结构体的定义。函数
struct event_config { TAILQ_HEAD(event_configq, event_config_entry) entries; int n_cpus_hint; enum event_method_feature require_features; enum event_base_config_flag flags; }; struct event_config_entry { TAILQ_ENTRY(event_config_entry) next; const char *avoid_method; };
咱们要作的就是对event_config结构体的那四个成员变量进行配置。oop
第一个成员entries,其结构就不展开了,关于TAILQ_HEAD,能够参考《TAILQ_QUEUE队列》。这里知道它是表示一个队列便可,队列元素的类型就是event_config_entry,能够用来存储一个字符串指针。它对应的设置函数为event_config_avoid_method。ui
Libevent是跨平台的Reactor,对于事件监听,其内部是使用多路IO复用函数。比较通用的多路IO复用函数是select和poll。而不少平台都提出了本身的高效多路IO复用函数,好比:epoll、devpoll、kqueue。Libevent对于这些多路IO复用函数都进行包装,供本身使用。event_config_avoid_method函数就是指出,避免使用指定的多路IO复用函数。其是经过字符串的方式指定的,即参数method。这个字符串将由队列元素event_config_entry的avoid_method成员变量存储(因为是指针,因此更准确来讲是指向)。this
查看Libevent源码包里的文件,能够发现有诸如epoll.c、select.c、poll.c、devpoll.c、kqueue.c这些文件。打开这些文件就能够发如今文件内容的前面都会定义一个struct eventop类型变量。该结构体的第一个成员必然是一个字符串。这个字符串就描述了对应的多路IO复用函数的名称。因此是能够经过名称来禁用某种多路IO复用函数的。
下面是event_config_avoid_method函数的实现。其做用是把method指明的各个名称记录到entries成员变量中。
int event_config_avoid_method(struct event_config *cfg, const char *method) { struct event_config_entry *entry = mm_malloc(sizeof(*entry)); if (entry == NULL) return (-1); //复制字符串 if ((entry->avoid_method = mm_strdup(method)) == NULL) { mm_free(entry); return (-1); } //插入到队列中 TAILQ_INSERT_TAIL(&cfg->entries, entry, next); return (0); }
上面的代码是设置拒绝使用某一个多路IO复用函数,在建立一个event_base时怎么进行选择的能够参考这一个连接。
第二个成员变量n_cpus_hint。从名字来看是指明CPU的数量。是经过函数event_config_set_num_cpus_hint来设置的。其做用是告诉event_config,系统中有多少个CPU,以便做一些对线程池做一些调整来获取更高的效率。目前,仅仅Window系统的IOCP(Windows的IOCP可以根据CPU的个数智能调整),该函数的设置才有用。在之后,Libevent可能会将之应用于其余系统。
正如其名字中的hint,这仅仅是一个提示。就如同C++中的inline。event_base实际使用的CPU个数不必定等于提示的个数。
第三个成员变量require_features。从其名称来看是要求的特征。不错,这个变量指定 多路IO复用函数应该知足哪些特征。全部的特征定义在一个枚举类型中。
//event.h文件 enum event_method_feature { //支持边沿触发 EV_FEATURE_ET = 0x01, //添加、删除、或者肯定哪一个事件激活这些动做的时间复杂度都为O(1) //select、poll是不能知足这个特征的.epoll则知足 EV_FEATURE_O1 = 0x02, //支持任意的文件描述符,而不能仅仅支持套接字 EV_FEATURE_FDS = 0x04 };
这个成员变量是经过event_config_require_features函数设置的。该函数的内部仍是挺简单的。
int event_config_require_features(struct event_config *cfg, int features) { if (!cfg) return (-1); cfg->require_features = features; return (0); }
从函数的实现能够看到,若是要设置多个特征,不能调用该函数屡次,而应该使用位操做。好比: EV_FEATURE_O1 | EV_FEATURE_FDS做为参数。
值得注意的是,对于某些系统,可能其提供的多路IO复用函数不能知足event_config_require_features函数要求的特征,此时event_base_new_with_config函数将返回NULL,即得不到一个知足条件的event_base。因此在设置这个特征时,那么就要检查event_base_new_with_config的返回值是否为NULL,像下面代码那样。
#include<event.h> #include<stdio.h> int main() { event_config *cfg = event_config_new(); event_config_require_features(cfg, EV_FEATURE_O1 | EV_FEATURE_FDS); event_base *base = event_base_new_with_config(cfg); if( base == NULL ) { printf("don't support this features\n"); base = event_base_new(); //使用默认的。 } ….. return 0; }
上面代码中,若是是在Linux运行,也是返回NULL。即epoll都不能同时知足那个两个特征。
那么怎么知道多路IO复用函数支持哪些特征呢?前面说到的一个机构体struct eventop中有一个成员正是enum event_method_feature features。在Libevent-2.0.21-stable中是倒数第二个成员。打开epoll.c、select.c、poll.c、devpoll.c、kqueue.c这些文件,查看里面定义的struct eventop类型变量,就能够看到各个多路IO复用函数都支持哪些特征。在epoll.c文件能够看到,epoll支持EV_FEATURE_ET|EV_FEATURE_O1。因此前面的代码中,返回NULL。
第四个变量flags是经过函数event_config_set_flag设置的。函数的实现很简单。注意,函数的内部是进行 |= 运算的。
//event.c文件 int event_config_set_flag(struct event_config *cfg, int flag) { if (!cfg) return -1; cfg->flags |= flag; return 0; }
如今来看一下参数flag能够取哪些值。
综观上面4个变量的设置,特征设置event_config_require_features和CPU数目设置event_config_set_num_cpus_hint二者的函数调用会覆盖以前的设置。若是要同时设置多个,那么须要在参数中使用位运算中的 | 。而另外两个变量的设置能够经过屡次调用函数的方式同时设置多个值。
前面的介绍的都是设置,如今来说一下获取。主要有下面几个。
const char **event_get_supported_methods(void); const char *event_base_get_method(const struct event_base *); int event_base_get_features(const struct event_base *base); static int event_config_is_avoided_method(const struct event_config *cfg, const char *method)
第一个函数是获取当前系统所支持的多路IO复用函数有哪些。第二个函数须要一个event_base结构体做为参数,说明是在new到一个event_base以后才能调用的。该函数返回值是对应event_base* 当前所采用的多路IO复用函数是哪一个。第三个函数则是获取参数event_base当前所采用的特征是什么。第四个函数则说明参数method指明的多路IO复用函数是否是被参数cfg所禁用了。若是是禁用了,返回非0值。不由用就返回0。
#include<event2/event.h> #include<stdio.h> #ifdef WIN32 #include<WinSock2.h> #endif int main() { #ifdef WIN32 WSADATA wsa_data; WSAStartup(0x0201, &wsa_data); #endif const char** all_methods = event_get_supported_methods(); while( all_methods && *all_methods ) { printf("%s\t", *all_methods++); } printf("\n"); event_base *base = event_base_new(); if( base ) printf("current method:\t %s\n", event_base_get_method(base) ); else printf("base == NULL\n"); #ifdef WIN32 WSACleanup(); #endif return 0; }
上面代码在个人Ubuntu10.04上运行,其结果为:
epoll poll select
currentmethod: epoll
在Win7 + VS2010的运行结果为
win32
currentmethod: win32
参考:
http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html