zoukankan      html  css  js  c++  java
  • 一起读读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

      

  • 相关阅读:
    Eclipse 安装C++
    工厂模式
    程序员7年和我的7点感想――我的程序人生
    Java中的==和equals区别
    编程之美1
    Java_Ant详解
    我用电脑说爱你
    Oracle分页的SQL语句
    使用js获取父窗口iframe的高度
    Oracle PLSQL中 左连接和右连接用法
  • 原文地址:https://www.cnblogs.com/hwy89289709/p/6979262.html
Copyright © 2011-2022 走看看