Nginx源码分析--模块module解析执行nginx.conf配置文件流程分析一

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Nginx源码分析--模块module解析执⾏nginx.conf配置⽂件流程分析

搭建nginx服务器时,主要的配置⽂件 nginx.conf 是部署和维护服务器⼈员经常要使⽤到的⽂件,⾥⾯进⾏了许多服务器参数的设置。

那么nginx 以模块 module为⾻架的设计下是如何运⽤模块 module来解析并执⾏nginx.conf配置⽂件下的指令的呢?在探究源码之前,需要对nginx下的模块 module 有个基本的认知(详情参考前⾯的博⽂ )同时也要
对nginx中常⽤到的⼀些结构有个基本的了解如:内存池pool 管理相关的函数、ngx_string 的基本结构等(详情参考前⾯的博⽂),若不然看代码的时候可能不能很明晰其中的意思,本⽂着重探究的是解析执⾏的流程。

1、从main函数说起。

Nginx的main函数在nginx.c⽂件中(本⽂使⽤release-1.3.0版本源码,200⾏),因为是主函数其中涉及到了许许多多的功能模块的初始化等内容,我们只关注我们需要的部分。

看到326⾏:
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
cycle = ngx_init_cycle(&init_cycle);
可以看出来,这⾥对 ngx_modules (中有介绍)进⾏了索引编号,并且计算得到模块的总数 ngx_max_module。

然后,对cycle进⾏初始化,跳转到 ngx_init_cycle中。


于cycle 这个变量是nginx的核⼼变量,可以说模块机制都是围绕它进⾏的,⾥⾯的参数⽐较复杂涉及到的内容⼗分多,本⽂并不详细对它讨论,可以将其看作是⼀个核⼼资源库。

2、ngx_init_cycle 函数
这个函数在⽂件ngx_cycle.c中(43⾏),这个函数是nginx初始化中最重要的函数之⼀,⾥⾯涉及到与cycle变量相关的初始化⼯作,看到第188⾏
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module *sizeof(void *));
这⾥获取了 ngx_max_module 个指针空间,⽤来保存每个模块的配置信息,从cycle 变量的字段conf_ctx 命名中就可以知道,ctx 为context 上下⽂的缩写。

接下来看到,下⾯这段:
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) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
意思就是获取模块中属于 NGX_CORE_MODULE 类的模块,如果需要创建配置信息就创建相应的配置信息,并且将地址保存在先前创建好的 cycle->conf_ctx 地址空间中,完成核⼼模块配置⽂件的创建过程,⾄此前期⼯作就基本完成了。

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;
#if 0
log->log_level = NGX_LOG_DEBUG_ALL;
#endif
if (ngx_conf_param(&conf) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
前⾯conf的赋值那段,⽆⾮是对conf进⾏些必要的初始化。

注意⼀下这⾥解析的都是对核⼼模块进⾏的,创建的配置⽂件也只是核⼼模块。

关键的函数开始出现
了:ngx_conf_param(&conf) 将conf需要的参数(可能没有就是空)存到conf中,ngx_conf_parse(&conf, &cycle->conf_file) 解析配置⽂件!
3、函数ngx_conf_parse 指令解析函数,关键函数!
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
char *rv;
ngx_fd_t fd;
ngx_int_t rc;
ngx_buf_t buf;
ngx_conf_file_t *prev, conf_file;
enum {
parse_file = 0,
parse_block,
parse_param
} type;
/*
该函数存在三种运⾏⽅式,并⾮⼀定需要打开配置⽂件
*/
#if (NGX_SUPPRESS_WARN)
fd = NGX_INVALID_FILE;
prev = NULL;
#endif
/*
filename 的值为 nginx.conf 的路径
*/
if (filename) {
/* 打开配置⽂件 */
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
...
/*
保存cf->conf_file 的上⽂
*/
prev = cf->conf_file;
/*
定义cf->conf_file 当前的变量信息
*/
cf->conf_file = &conf_file;
/*
接下来是对,conf_file 的参数进⾏设置,为了⽅便阅读省略此处代码
*/
...
/*
将函数的运⾏模式定位为 parse_file ,配置⽂件模式。

*/
type = parse_file;
/*
其它两个else 是定义其他模式,在解析nginx.conf时并不会使⽤到
*/
} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
type = parse_block;
} else {
type = parse_param;
}
/*
完成对配置⽂件信息的,初步设置之后,就开始对配置⽂件进⾏解析。

*/
for ( ;; ) {
/*
获取从配置⽂件nginx.conf中读取的指令名,对于 ngx_conf_read_token 下⾯给出来返回参数的详细英⽂注释 */
rc = ngx_conf_read_token(cf);
/*
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/
/*
如果错误,调转到done处执⾏
*/
if (rc == NGX_ERROR) {
goto done;
}
/*
如果如到“}”符号,跳转到done处执⾏,出现错误跳到failed处
*/
if (rc == NGX_CONF_BLOCK_DONE) {
if (type != parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
goto failed;
}
goto done;
}
/*
如果配置⽂件全部解析完成,调转到done处执⾏。

*/
if (rc == NGX_CONF_FILE_DONE) {
if (type == parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting \"}\"");
goto failed;
}
goto done;
}
/*
如果遇到“{"但出现错误,调转到failed 处执⾏
*/
if (rc == NGX_CONF_BLOCK_START) {
if (type == parse_param) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"block directives are not supported "
"in -g option");
goto failed;
}
}
/*
前⾯对可能出现的情况都进⾏了相应的跳转,那么剩下的就是读取指令正确后执⾏的过程了,主要分为两种,⼀种为NGX_OK ⼀般指令的进⾏如:worker_processes 另⼀种 NGX_CONF_BLOCK_START 就是以{作为结束符指令的执⾏,如:events、http 这类有⼆级指令的。

rc == NGX_OK || rc == NGX_CONF_BLOCK_START
*/
if (cf->handler) {
/*
指令执⾏前是否要进⾏些处理⼯作
* the custom handler, i.e., that is used in the http's
* "types { ... }" directive
*/
rv = (*cf->handler)(cf, NULL, cf->handler_conf);
if (rv == NGX_CONF_OK) {
continue;
}
if (rv == NGX_CONF_ERROR) {
goto failed;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
goto failed;
}
/*
下⼀个关键函数 ngx_conf_handler
*/
rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) {
goto failed;
}
}
failed:
rc = NGX_ERROR;
done:
/*
⼀些完成后的处理,释放资源或者出错处理。

省略
*/
...
/*
恢复上下⽂
*/
cf->conf_file = prev;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
在以上代码中,除了将关键函数⽤红⾊标记以外,还特意将函数中对上下⽂的保存和还原⼯作的地⽅进⾏了红⾊标记,因为在nginx源码中经常使⽤到这种机制,可以记住下这样的写法。

4、函数ngx_conf_handler 指令处理函数,关键函数!
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
...
for (i = 0; ngx_modules[i]; i++) {
/* 查找与指令想对应的模块 module*/
if (ngx_modules[i]->type != NGX_CONF_MODULE
&& ngx_modules[i]->type != cf->module_type)
{
continue;
}
/*
读取模块的指令集
*/
cmd = ngx_modules[i]->commands;
if (cmd == NULL) {
continue;
}
for ( /* void */ ; cmd->name.len; cmd++) {
/*
遍历指令集中的指令,并找寻从配置⽂件中读取到的指令相对应的内容
*/
if (name->len != cmd->name.len) {
continue;
}
if (ngx_strcmp(name->data, cmd->name.data) != 0) {
continue;
}
/* 判断下指令类型是否正确*/
if (!(cmd->type & cf->cmd_type)) {
if (cmd->type & NGX_CONF_MULTI) {
multi = 1;
continue;
}
goto not_allowed;
}
...
/*判断指令参数是否正确*/
if (!(cmd->type & NGX_CONF_ANY)) {
if (cmd->type & NGX_CONF_FLAG) {
if (cf->args->nelts != 2) {
goto invalid;
}
} else if (cmd->type & NGX_CONF_1MORE) {
}
...
}
/*
通过指令的类型,来设置执⾏指令时需要的模块前期创建的 cf_ctx⾥⾯的配置信息,朔源就是 cycle->conf_ctx 当然它指向的上下⽂可能已经发⽣了改变
*/
conf = NULL;
if (cmd->type & NGX_DIRECT_CONF) { conf = ((void **) cf->ctx)[ngx_modules[i]->index]; }
...
/*
执⾏指令对应的功能函数!!
*/
rv = cmd->set(cf, cmd, conf);
/*
如果执⾏成功,返回成功。

*/
if (rv == NGX_CONF_OK) {
return NGX_OK;
}
/*
⾄此,配置⽂件的指令执⾏就结束了。

后⾯都是⼀些出错处理,在此省略。

*/
...
}
}
...
}
写到这⾥时间已经有些晚了,⼩结⼀下。

通过代码摘录的介绍,将整个nginx.conf解析的流程概括的演⽰了出来,对于其中的些地⽅可能还不明晰如:⼆级模块的指令是如何执⾏的(就是 events{ ... }、http{ ... } 括号⾥⾯的指令如何执⾏的)、⾮核⼼模块是如何加⼊对 nginx.conf 这个配置⽂件进⾏解析等⼀些内容,在后⾯的分析中再写吧。

晚安!。

相关文档
最新文档