Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 二
接上一篇,通过关键代码段的分析、和一些关键函数的摘录对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 配置文件的解析过程分析就全部结束了,写些程序的时候可以借鉴下这样的模块 解析配置文件的 构架。