专栏首页原创分享nginx0.1.0之event模块初始化源码分析(1)

nginx0.1.0之event模块初始化源码分析(1)

下面是nginx中几个重要的数据结构。

struct ngx_command_s {
    ngx_str_t     name; // 命令名字
    int           type; // 命令的属性,参数个数、上下文等
    char       *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); // 处理该命令的函数
    int           conf;
    int           offset;
    void         *post;
};

struct {
    ngx_str_t       name;
    void         *(*create_conf)(ngx_cycle_t *cycle);
    char         *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t; 

struct ngx_module_s {
    ngx_uint_t       ctx_index;
    ngx_uint_t       index;
    void            *ctx;
    ngx_command_t   *commands;
    ngx_uint_t       type;
    ngx_int_t      (*init_module)(ngx_cycle_t *cycle);
    ngx_int_t      (*init_process)(ngx_cycle_t *cycle);
#if 0
    ngx_int_t      (*init_thread)(ngx_cycle_t *cycle);
#endif
};

下面是event模块的配置

static ngx_command_t  ngx_events_commands[] = {

    { ngx_string("events"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_events_block,
      0,
      0,
      NULL },

      ngx_null_command
};


static ngx_core_module_t  ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    NULL
};  


ngx_module_t  ngx_events_module = {
    NGX_MODULE,
    &ngx_events_module_ctx,                /* module context */
    ngx_events_commands,                   /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init module */
    NULL                                   /* init process */
};

nginx模块初始化的流程在下面的代码中,核心模块的初始化,各核心模块首先在create_conf中创建保存配置的数据结构,然后在ngx_conf_parse中,通过解析命令,执行对应的命令处理函数,完成赋值和各核心模块的子模块初始化。最后,如果在ngx_conf_parse时,没有设置值,则执行init_conf函数进行默认初始化。

// 执行核心模块的钩子函数,该版本只有ngx_core_module模块定义了这个钩子
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;
        // 用于存储各类型模块下子模块的配置
        if (module->create_conf) {
            // 分配一块内存存储子模块的数据结构,如ngx_core_module_create_conf函数
            rv = module->create_conf(cycle);
            if (rv == NGX_CONF_ERROR) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[ngx_modules[i]->index] = rv;
        }
    }

    // 初始化保存指令信息的结构体
    ngx_memzero(&conf, sizeof(ngx_conf_t));
    /* STUB: init array ? */
    conf.args = ngx_create_array(pool, 10, sizeof(ngx_str_t));
    if (conf.args == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
    }
    // 指向所有模块的上下文
    conf.ctx = cycle->conf_ctx;
    conf.cycle = cycle;
    conf.pool = pool;
    conf.log = log;
    conf.module_type = NGX_CORE_MODULE;
    conf.cmd_type = NGX_MAIN_CONF;

    // 解析配置文件,把结果写入conf
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
        ngx_destroy_pool(pool);
        return NULL;
    }

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

        module = ngx_modules[i]->ctx;
        // 如ngx_core_module_init_conf函数
        if (module->init_conf) {
            // cycle->conf_ctx[ngx_modules[i]->index]由crete_init函数创建
            if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
                                                              == NGX_CONF_ERROR)
            {
                ngx_destroy_pool(pool);
                return NULL;
            }
        }
    }
... // 配置处理完,开始初始化各个模块
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_module) {
            if (ngx_modules[i]->init_module(cycle) == NGX_ERROR) {
                /* fatal */
                exit(1);
            }
        }
    }

从上面的代码中我们知道event模块没有实现create_conf和init_conf函数,那么event模块初始化的时机是什么时候?答案是在解析到event命令的时候。由event模块对应的配置可知,当解析到event命令的时候,会执行ngx_events_block函数。代码如下。

static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    int                    m;
    char                  *rv;
    void               ***ctx;
    ngx_conf_t            pcf;
    ngx_event_module_t   *module;

    /* count the number of the event modules and set up their indices */

    ngx_event_max_module = 0;
    // 初始化每个子模块的序号
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_event_max_module++;
    }
    // ctx指向一个指针
    ngx_test_null(ctx, ngx_pcalloc(cf->pool, sizeof(void *)), NGX_CONF_ERROR);
    // ctx指向的指针再指向一个指针数组
    ngx_test_null(*ctx,
                  ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *)),
                  NGX_CONF_ERROR);
    // event是NGX_MAIN_CONF类型的模块,conf为四级指针
    *(void **) conf = ctx;

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }
        // event类型的模块的ctx
        module = ngx_modules[m]->ctx;
        // 把create_conf返回的数据结构存储在上面开辟的数组里
        if (module->create_conf) {
            ngx_test_null((*ctx)[ngx_modules[m]->ctx_index],
                          module->create_conf(cf->cycle),
                          NGX_CONF_ERROR);
        }
    }
    // event模块都是基于下面的上下文配置进行命令的解析
    pcf = *cf;
    // 修改当前的上下文和作用域信息
    cf->ctx = ctx;
    // 用于过滤模块类型,即接下来的配置解析中,等于该类型的模块才能处理该命令
    cf->module_type = NGX_EVENT_MODULE;
    /*
        解析出一个配置的时候,如果匹配到了某个命令配置,则该命令配置是否是属于NGX_EVENT_CONF类型,
        用于二级 过滤,即过滤掉同模块里的不符合条件的模块
    */
    cf->cmd_type = NGX_EVENT_CONF;
    // 继续解析,对event模块的子模块的字段进行赋值
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf;

    if (rv != NGX_CONF_OK)
        return rv;
    // 如果ngx_conf_parse没有进行赋值,则执行init_conf函数时进行默认初始化
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        // 初始化create_conf创建的结构体
        if (module->init_conf) {
            // 如果在ngx_conf_parse里没有对模块的配置进行初始化则这里进行默认初始化,一般是在cmd的set函数进行初始化
            rv = module->init_conf(cf->cycle,
                                   (*ctx)[ngx_modules[m]->ctx_index]);
            if (rv != NGX_CONF_OK) {
                return rv;
            }
        }
    }

    return NGX_CONF_OK;
}

本文分享自微信公众号 - 编程杂技(theanarkh)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • nginx0.1.0之event模块初始化源码分析(3)

    前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历...

    theanarkh
  • nginx0.1.0之http模块初始化源码分析(2)

    本文讲解http各个模块create_srv_conf和create_loc_conf钩子,还有指令的解析。 各模块的create_srv_conf和creat...

    theanarkh
  • nginx1.17.9源码分析之管理配置的结构体(1)

    之前对nginx0.1.0版本进行了部分代码的分析,接下来的时间,打算以最新版的源码为基础,重新开始分析nginx的实现。这是第一篇。

    theanarkh
  • 利用NFV和SDN实现5G网络切片

    网络切片 网络切片将在5G的实施中发挥关键作用,该技术允许运营商在单一的物理基础设施之上运行多个虚拟网络。随着2020年的5G商业化,许多人正在想,网络功能虚拟...

    企鹅号小编
  • 利用NFV和SDN实现5G网络切片

    网络切片 网络切片将在5G的实施中发挥关键作用,该技术允许运营商在单一的物理基础设施之上运行多个虚拟网络。随着2020年的5G商业化,许多人正在想,网络功能虚拟...

    SDNLAB
  • 面对SDN/NFV部署挑战 网络厂商能做什么?

    近年来,随着云计算的不断发展,网络虚拟化受到广泛关注,而作为网络虚拟化的实现方式,SDN自诞生之日起就担负着网络变革的使命。那么,SDN技术的驱动力是什么?SD...

    SDNLAB
  • 新手服务器指南:远程服务器复制文件到本地电脑

    无论是远程服务器复制到本地,还是本地电脑向服务器上传文件都是非常方便的,本教程适用于Windows服务器。

    网站运营忧哥
  • Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据

    在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题:

    Java中文社群_老王
  • 怎样将本地web项目部署到腾讯云服务器上?

    (1).用eclipse新建一个web项目,然后在webcontent下新建一个index.html,然后在本地部署到Tomcat服务器下,打开浏览器看是否能访...

  • SDN与NFV技术在云数据中心的规模应用探讨

    编者按:以云数据中心为切入点,首先对SDN领域中的叠加网络、SDN控制器、VxLAN 3种重要技术特点进行了研究,接下来对NFV领域中的通用服务器性能、服务链两...

    SDNLAB

扫码关注云+社区

领取腾讯云代金券