一起读读libevent的源代码:Libevent 第二章 创建Event base (1)

在您可以使用任何有趣的libevent函数,需要分配一个或多个event_base结构。每个event_base结构持有一组事件,可以poll决定哪些事件是激活的。

如果一个event_base设置使用lock,可以访问多个线程之间。然而,它的循环只能在单个线程中运行。如果你想有多个线程轮询IO,你需要为每个线程一个event_base。

支持一下的方法来进行决定哪个是激活的:

  • select

  • poll

  • epoll

  • kqueue

  • devpoll

  • evport

  • win32

用户可以禁用特定的后端(上面的方法就称为backend)与环境变量。如果你想关闭kqueue后端,设置event_nokqueue环境变量( EVENT_NOEPOLLEVENT_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,这几个的分别的意思是:

  1. max_dispatch_callbacks:event_base在两次检查新事件之间的执行回调函数个数的最大个数,  
  2. max_dispatch_interval: event_base在两次检查新事件之间消耗的最大时间。  
  3. 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

  

原文地址:https://www.cnblogs.com/hwy89289709/p/6979262.html