nginx源码分析(4)——事件模型

        对于一个服务器来说,事件模型是至关重要的,nginx本身的高性能也要归功于其优秀的事件模型。一般地,nginx的事件模型是基于epoll的,而使用epoll需要调用epoll_create、epoll_ctl和epoll_wait三个函数。由于nginx本身的松耦合模块机制,这些函数的调用被隐藏在很难发现的地方,本篇文章就来介绍nginx的事件模型的初始化过程,从而大家可以清晰的知道epoll各个函数的具体调用位置。

1. 重要的数据结构

        1. ngx_event_actions_t

        这个结构是nginx底层事件模型的抽象,具体的io模型会有自己的实现,比如epoll、select。通过这一层的抽象屏蔽了底层的不同实现,我们可以轻易从一种模型迁移至其他模型。这实际上就是C的面向接口编程,值得学习。在ngx_event.c中定义了全局变量ngx_event_actions,他指向具体的底层实现,在底层模型init函数中被赋值。

typedef struct {
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                   ngx_uint_t flags);

    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;


2. 事件模型

        在前文中已经介绍过,ngx_events_module是一个core module,由它来完成event module的初始化。当我们查看objs/ngx_modules.c文件中的ngx_moduels数组(保存所有nginx模块)时,会发现只有两个event module,分别是ngx_event_core_module和ngx_epoll_module。ngx_event_core_module这个模块在事件模型初始化过程中起着至关重要的作用,而ngx_epoll_module实际上就是底层io模型的实现。事件模型的初始化与http模块类似,由ngx_events_module驱动整个事件模块的解析和初始化,ngx_event_core_module对events块大部分指令的解析保存重要的配置信息。下面就来具体看看事件模型的初始化,这里依然采用之前的方式——按照nginx执行的流程,也就是事件模型的初始化顺序。

        1. ngx_events_block

        与ngx_http_module类似,ngx_events_module只有一个指令:events,这是一个块指令,而它的回调函数ngx_events_block就是事件模型初始化的入口。  

    ngx_event_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        ngx_modules[i]->ctx_index = ngx_event_max_module++;
    }
        还是与http模块一样,对所有的event module计数,同时更新模块的ctx_index,还记得这个变量吗?它就是该模块的配置结构在具体类型(http、event...)配置结构数组中的下标。

 

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    if (*ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /**
     * 将在ngx_cycle->conf_ctx数组中存放的ngx_events_module的config信息赋值为ctx
     * 也就是所有event module配置信息的数组。
     */
    *(void **) conf = ctx;
        为ctx分配空间,所有event module的全局配置就是一个数组,这里也为它分配空间,同时将存放在ngx_cycle->conf_ctx数组的ngx_events_module的配置结构赋值为ctx。


    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;

        if (m->create_conf) {
            (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
            if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }
        调用所有event module的create_conf回调函数创建配置结构。


    /**
     * 为解析events块准备,设置要解析的模块以及指令的类型。
     * 备份cf,解析完events块后恢复。
     */
    pcf = *cf;
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    /**
     * 解析events块
     */
    rv = ngx_conf_parse(cf, NULL);
        开始解析events块。前面已经介绍过nginx中只有ngx_event_core_module和ngx_epoll_module两个event module。解析events块就是解析这两个模块的指令,而它们的指令主要是一些配置信息,比如worker_connection指令设置每个worker的连接数,实际上就是为ngx_cycle->connection_n赋值而已,accept_mutex指令就是指定是否使用accept锁,所以这部分内容并不影响事件模块的初始化,所以这不再赘述。

        

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;

        if (m->init_conf) {
            rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
            if (rv != NGX_CONF_OK) {
                return rv;
            }
        }
    }
        解析完events块,接着调用所有event module的init_conf回调函数初始化模块的配置结构。这里ngx_event_core_module和ngx_epoll_module会对配置结构中尚未初始化的一些属性赋默认值,比如默认使用io模型,也就是use指令的默认值。

        看到这里,大家可能好奇events块已经解析完毕

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值