在您可以使用任何有趣的libevent函数,需要分配一个或多个event_base结构。每个event_base结构持有一组事件,可以poll决定哪些事件是激活的。
如果一个event_base设置使用lock,可以访问多个线程之间。然而,它的循环只能在单个线程中运行。如果你想有多个线程轮询IO,你需要为每个线程一个event_base。
支持一下的方法来进行决定哪个是激活的:
-
select
-
poll
-
epoll
-
kqueue
-
devpoll
-
evport
-
win32
用户可以禁用特定的后端(上面的方法就称为backend)与环境变量。如果你想关闭kqueue后端,设置event_nokqueue环境变量( EVENT_NOEPOLL, EVENT_NOKQUEUE, EVENT_NODEVPOLL, EVENT_NOPOLL or EVENT_NOSELECT, ),等等.
EVENT_SHOW_METHOD 可以显示有哪些 kernel notification method 。
如果你想在程序里面关闭某个后端, 可以查看这个函数:event_config_avoid_method
1137 int 1138 event_config_avoid_method(struct event_config *cfg, const char *method) 1139 { 1140 struct event_config_entry *entry = mm_malloc(sizeof(*entry)); 1141 if (entry == NULL) 1142 return (-1); 1143 1144 if ((entry->avoid_method = mm_strdup(method)) == NULL) { 1145 mm_free(entry); 1146 return (-1); 1147 } 1148 1149 TAILQ_INSERT_TAIL(&cfg->entries, entry, next); 1150 1151 return (0); 1152 }
这个函数,就是要把这个避免配置 struct event_config_entry 插入 struct event_config 的队列里面:查看下这两个结构体的定义,大概猜想就是创建一个event_base的时候,会使用这些配置文件,所以只要设置如就可以避免使用这些后段方法吧?
0357 /** Internal structure: describes the configuration we want for an event_base 0358 * that we're about to allocate. */ 0359 struct event_config { 0360 TAILQ_HEAD(event_configq, event_config_entry) entries; 0361 0362 int n_cpus_hint; 0363 struct timeval max_dispatch_interval; 0364 int max_dispatch_callbacks; 0365 int limit_callbacks_after_prio; 0366 enum event_method_feature require_features; 0367 enum event_base_config_flag flags; 0368 }; 0351 struct event_config_entry { 0352 TAILQ_ENTRY(event_config_entry) next; 0353 0354 const char *avoid_method; 0355 };
Setting up a default event_base
接口:
Interface struct event_base *event_base_new(void);
函数的定义如下:
0479 struct event_base * 0480 event_base_new(void) 0481 { 0482 struct event_base *base = NULL; 0483 struct event_config *cfg = event_config_new(); 0484 if (cfg) { 0485 base = event_base_new_with_config(cfg); 0486 event_config_free(cfg); 0487 } 0488 return base; 0489 }
可以看出,这几步分别是:创建一个新的配置给cfg,然后cfg传给event_base_new_with_config()来进行创建,最后再释放调cfg。下面会一个一个这些函数。
1092 struct event_config * 1093 event_config_new(void) 1094 { 1095 struct event_config *cfg = mm_calloc(1, sizeof(*cfg)); 1096 1097 if (cfg == NULL) 1098 return (NULL); 1099 1100 TAILQ_INIT(&cfg->entries); 1101 cfg->max_dispatch_interval.tv_sec = -1; 1102 cfg->max_dispatch_callbacks = INT_MAX; 1103 cfg->limit_callbacks_after_prio = 1; 1104 1105 return (cfg); 1106 }
代码里看出,默认的配置是听见但的。只设置了几个简单的属性,把这个cfg初始化到了双向链表。除了以下几项,其余的都为0. max_dispatch_interval, max_dispatch_callbacks, limit_callbacks_after_prio,这几个的分别的意思是:
- max_dispatch_callbacks:event_base在两次检查新事件之间的执行回调函数个数的最大个数,
- max_dispatch_interval: event_base在两次检查新事件之间消耗的最大时间。
- limit_callbacks_after_prio:是只有优先级数字>=limit_callbacks_after_prio的事件触发时,才会强迫
然后下面是event_base_new_with_config()函数的定义:里面有特别多的函数,分开一个一个介绍:
0564 struct event_base * 0565 event_base_new_with_config(const struct event_config *cfg) 0566 { 0567 int i; 0568 struct event_base *base; 0569 int should_check_environment; 0570 0571 #ifndef EVENT__DISABLE_DEBUG_MODE 0572 event_debug_mode_too_late = 1; 0573 #endif 0574 0575 if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) { 0576 event_warn("%s: calloc", __func__); 0577 return NULL; 0578 } 0579 0580 if (cfg) 0581 base->flags = cfg->flags; 0582 0583 should_check_environment = 0584 !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV)); 0585 0586 { 0587 struct timeval tmp; 0588 int precise_time = 0589 cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER); 0590 int flags; 0591 if (should_check_environment && !precise_time) { 0592 precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL; 0593 base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER; 0594 } 0595 flags = precise_time ? EV_MONOT_PRECISE : 0; 0596 evutil_configure_monotonic_time_(&base->monotonic_timer, flags); 0597 0598 gettime(base, &tmp); 0599 } 0600 0601 min_heap_ctor_(&base->timeheap); 0602 0603 base->sig.ev_signal_pair[0] = -1; 0604 base->sig.ev_signal_pair[1] = -1; 0605 base->th_notify_fd[0] = -1; 0606 base->th_notify_fd[1] = -1; 0607 0608 TAILQ_INIT(&base->active_later_queue); 0609 0610 evmap_io_initmap_(&base->io); 0611 evmap_signal_initmap_(&base->sigmap); 0612 event_changelist_init_(&base->changelist); 0613 0614 base->evbase = NULL; 0615 0616 if (cfg) { 0617 memcpy(&base->max_dispatch_time, 0618 &cfg->max_dispatch_interval, sizeof(struct timeval)); 0619 base->limit_callbacks_after_prio = 0620 cfg->limit_callbacks_after_prio; 0621 } else { 0622 base->max_dispatch_time.tv_sec = -1; 0623 base->limit_callbacks_after_prio = 1; 0624 } 0625 if (cfg && cfg->max_dispatch_callbacks >= 0) { 0626 base->max_dispatch_callbacks = cfg->max_dispatch_callbacks; 0627 } else { 0628 base->max_dispatch_callbacks = INT_MAX; 0629 } 0630 if (base->max_dispatch_callbacks == INT_MAX && 0631 base->max_dispatch_time.tv_sec == -1) 0632 base->limit_callbacks_after_prio = INT_MAX; 0633 0634 for (i = 0; eventops[i] && !base->evbase; i++) { 0635 if (cfg != NULL) { 0636 /* determine if this backend should be avoided */ 0637 if (event_config_is_avoided_method(cfg, 0638 eventops[i]->name)) 0639 continue; 0640 if ((eventops[i]->features & cfg->require_features) 0641 != cfg->require_features) 0642 continue; 0643 } 0644 0645 /* also obey the environment variables */ 0646 if (should_check_environment && 0647 event_is_method_disabled(eventops[i]->name)) 0648 continue; 0649 0650 base->evsel = eventops[i]; 0651 0652 base->evbase = base->evsel->init(base); 0653 } 0654 0655 if (base->evbase == NULL) { 0656 event_warnx("%s: no event mechanism available", 0657 __func__); 0658 base->evsel = NULL; 0659 event_base_free(base); 0660 return NULL; 0661 } 0662 0663 if (evutil_getenv_("EVENT_SHOW_METHOD")) 0664 event_msgx("libevent using: %s", base->evsel->name); 0665 0666 /* allocate a single active event queue */ 0667 if (event_base_priority_init(base, 1) < 0) { 0668 event_base_free(base); 0669 return NULL; 0670 } 0671 0672 /* prepare for threading */ 0673 0674 #if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) 0675 event_debug_created_threadable_ctx_ = 1; 0676 #endif 0677 0678 #ifndef EVENT__DISABLE_THREAD_SUPPORT 0679 if (EVTHREAD_LOCKING_ENABLED() && 0680 (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) { 0681 int r; 0682 EVTHREAD_ALLOC_LOCK(base->th_base_lock, 0); 0683 EVTHREAD_ALLOC_COND(base->current_event_cond); 0684 r = evthread_make_base_notifiable(base); 0685 if (r<0) { 0686 event_warnx("%s: Unable to make base notifiable.", __func__); 0687 event_base_free(base); 0688 return NULL; 0689 } 0690 } 0691 #endif 0692 0693 #ifdef _WIN32 0694 if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP)) 0695 event_base_start_iocp_(base, cfg->n_cpus_hint); 0696 #endif 0697 0698 return (base); 0699 }
event_base部分:
如果有配置文件,将cfg->flags的标志,放到base->flags里面。
should_check_environment,根据cfg存在,以及cfg->flags的标志是否存在 EVENT_BASE_FLAG_IGNORE_ENV 标志来判断。按照默认配置的话,会被设置为0. EVENT_BASE_FLAG_IGNORE_ENV 是用于,当选择哪个后端的方法去使用的时候,不要检查EVENT_* 环境变量。
EVENT_BASE_FLAG_PRECISE_TIMER:默认情况下,libevent试图使用可用的最快的定时机制,操作系统提供。如果有一个较慢的定时机制提供更细粒度的定时精度,这个标志告诉libevent使用这种定时机制。
时间部分:
根据 EVENT_BASE_FLAG_PRECISE_TIMER 标志 和 cfg 里面的标志,决定是否使用更加准确的时间方法。默认的标志,当然是不使用了。precise_time = 0
如果 should_check_environment 为 1, 并且 precise_time 为0, 会进入一个if 语句, 再这个语句里面,precise_time 会等于 是否存在一个环境变量“EVENT_PRECISE_TIMER” 的真假只,一般是会为假的。也就是precise_time = 0,然后 base->flags 会添加这个 EVENT_BASE_FLAG_IGNORE_ENV 标志。
由于precise_time 为0 (根据默认配置),flags 还是会设置为0.
evutil_configure_monotonic_time_ ,HAVE_WIN32_MONOTONIC的版本是再470行,337的是 HAVE_MACH_MONOTONIC) 的是MAC的版本, HAVE_POSIX_MONOTONIC 是在262行,linux是支持这个的。 HAVE_FALLBACK_MONOTONIC 的版本是在 559. 这个函数主要是设置了 monotonic_timer 里面的 monotonic_clock 这个参数,主要证明是使用哪个函数。
最后一个函数是gettime(),tv_cache 这是用来避免经常调用 gettimeofday/clock_gettime;evutil_gettime_monotonic_()这个函数Linux的版本是在303行,主要是调用 clock_gettime()(因为 base->monotonic_clock 在支持的系统上没有小于0) 设置tp的时间。
其他部分:
然后调用了一个minheap,从名字来看,应该是一个堆。这里是创建一个最小堆。
初始化base:
内部信号通知的管道base->sig.ev_signal_pair ,0读1写 ,不用的话,先初始化为-1.
内部线程通知的文件描述符base->th_notify_fd ,0读1写,先初始化为-1.
初始化base->active_later_queue。
初始化 evmap_io :后面在分析这个
初始化evmap_signal:后面分析。
初始化event_changelist:后面分析:
初始化base->evbase 为NULL
根据cfg部分配置base:
复制cfg->max_dispatch_interval 到base,否则 max_dispatch_time.tv_sec 初始化为-1, limit_callbacks_after_prio 为1
如果cfg存在,并且cfg->max_dispatch_callbacks >= 0 就会拷贝,否则会自动初始化为INT_MAX
判断上面是否都是代码默认的配置,如果是 base->limit_callbacks_after_prio 会重新设置为 INT_MAX 。就是没有事件会被触发。
struct eventop 是定义了一个给定的event_base的后端。evbase是一个指向。 这里出现一个遍历eventops这个结构(并且要base->evbase里面没有数据),event_config_is_avoided_method 这个是遍历所给的cfg ,寻找锁避免的方法是否为这个。如果是,就会跳过这种方法。event_is_method_disabled ()是在 should_check_environment 为 真的时候会调用,如果确定eventopts的名字是disable的,就会跳过下面的设置。后面就是设置base-evsel,以及evbase。
如果evbase为NULL,就会报错,然后重新设置,因为这是没有可用的提取机制。
判断是否有环境变量EVENT_SHOW_METHOD,然后就会显示锁用的名字。event_base_priority_init() (初始化优先级的时候,要注意有没激活的事件等等)
最后一个是按照thread的分配。后面讲
evmap_io
evmap_signal
event_changelist