接上一篇,通过关键代码段的分析、和一些关键函数的摘录对nginx 运用module 模块构架对nginx.conf 进行解析执行的流程有了一个基本认识。流程总结如下:

  1、获取全部参与编译的模块module 进行统计编号。

  2、根据module 模块的个数分配 配置信息资源的指针空间。

  3、创建NGX_CORE_MODULE 核心模块的配置信息,并将指针空间中对应模块编号的指针进行设置。

  4、初始化conf 配置信息,解析参数 ngx_conf_param ,解析配置文件 ngx_conf_parse

  

  以上第4步中解析配置文件 ngx_conf_parse 过程又可分为:

  1、获取配置文件。

  2、保存当前配置文件的上下文,并将cf->conf_file 指向当前配置文件。

  3、读取当前配置文件中的配置指令名 ngx_conf_read_token 

  4、判断读取指令的类别、是否正确。

  5、执行指令前是否进行其他处理。

  6、交给 ngx_conf_handler 处理指令。

  7、全部执行完后,恢复上下文。

  以上第6 ngx_conf_handler 过程又可分解为:

  1、查找与配置信息中指定分析模块的类别,并获取该模块的指令集。

  2、遍历指令集是否有要求处理的指令。

  3、如果匹配,判断指令类型、指令参数是否正确。

  4、执行指令对应的功能函数 set

  5、正确完成返回NGX_OK

  整个流程一环扣一环,利用其中的
cf
ngx_conf_t
这个结构起到了穿针引线、传递资源的作用。分析到这里,
NGX_CORE_MODULE中的指令是知道如何执行的了,但是像https{
… }
events{
… }
括号中的指令是如何执行的呢?它可并非属于核心模块里面的指令。我们这里以events模块进行分析。

  我们首先来看events
核心指令的一些参数

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
};

  里面的 NGX_CONF_BLOCK 有没有印象?想想 ngx_conf_read_token() 里面返回的参数有NGX_CONF_BLOCK_START NGX_CONF_BLOCK_DONE 再联系下ngx_conf_handler 里面的这句:

if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { … }

  明白了吧,这个参数的意识就代表了,它是二级模块的标示,他后面跟着的是 { 这个符号作为结束符。这个指令参数也告诉我们,解析到events 这个标识 时调用 ngx_events_block。那就来看看 ngx_events_block 吧。

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

    /*统计 envent 模块总数,并建立编号*/
  /* 感觉相似么? 上一篇中的 main 函数里的那段代码!! */
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++; } ctx = ngx_pcalloc(cf->pool, sizeof(void *)); if (ctx == NULL) { return NGX_CONF_ERROR; }
/* 上一篇又有相似的, ngx_init_cycle 函数 里的那句!! */
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) { return NGX_CONF_ERROR; } *(void **) conf = ctx;
 /*  还是 ngx_init_cycle 函数 里有相似的!!!  */
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;
            }
        }
    }

/* 保存原来的cf ,设置当前的cf ,这样 cf 就变成二级模块的了!!不再是核心模块了!*/ pcf
= *cf; cf->ctx = ctx; cf->module_type = NGX_EVENT_MODULE; cf->cmd_type = NGX_EVENT_CONF;
/* 解析二级模块指令了!用的还是
ngx_conf_parse(上一篇) ,注意 cf 变了上面重新设置了*/
    rv = ngx_conf_parse(cf, NULL);
/* 恢复cf */
*cf = pcf; if (rv != NGX_CONF_OK) return rv;

  /* 初始化,
ngx_init_cycle 函数 里也有类似的。*/
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;
            }
        }
    }

    return NGX_CONF_OK;
}

  看完这段代码我想大家应该是熟悉的,里面很多和前一篇中摘录的代码相似,具体的在注释中也点明了 就像是一级模块解析的一个缩影。总结来说,对于像events{ .... }这样的二级模块,都有对应的ngx_XXXX_block 它在这里所做的工作就是,保存传进来的配置内容,设置二级模块需要的配置内容(再说具体一点就是配置二级模块类型,配置命令类型,重新定义上下文),然后解析括号中的二级模块指令用的还是 ngx_conf_parse 这个函数,这样一来就与一级模块指令的解析一致了。而三级模块也就类似可以解决了。至此,nginx 运用 module 模块构建对 nginx.conf 配置文件的解析过程分析就全部结束了,写些程序的时候可以借鉴下这样的模块 解析配置文件的 构架。