leevis.com
leevis.com copied to clipboard
nginx配置文件解析
前言
最近,又再重复着看nginx的代码。越看越激动,设计的如此精妙。代码写的也非常的好。 nginx是个非常强大的web服务器、反向代理服务器。他的强大之处离不开他的配置文件。那么配置文件是如何解析的呢?又是通过什么数据结构存放的呢?
解析流程
首先,c函数从main
开始执行,在main
函数调用了ngx_init_cycle
函数。在该函数中初始化了一个conf_ctx
指针数组用来存放各个模块的配置文件结构体指针,随后又调用了所有NGX_CORE_MODULE
模块的create_conf
创建核心模块配置结构体。最后通过调用两个方法来解析命令行配置和文件配置指令。这两个函数分别是:ngx_conf_param
和 ngx_conf_parse
。前面是解析命令行配置参数,后面是解析文件配置参数。前面的函数最终还是调用了后面的,那我们直接看后面的函数,如果传入配置文件结构体指针不为空,那么打开配置文件读取配置文件指令。最后调用ngx_conf_handler
解析配置指令,该方法才是解析配置文件最核心的方法,会根据配置指令遍历所有模块找到能解析该指令的方法,找到后调用对应模块的set方法。
解析细节说明
上述从整体流程介绍了一下解析配置文件的过程。本节通过具体的函数说明一下。
./core/nginx.c:main
......
//根据编译生成的模块数组初始化每个模块的序号index。同时把模块的个数赋值给ngx_max_module
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
cycle = ngx_init_cycle(&init_cycle);
if (cycle == NULL) {
if (ngx_test_config) {
ngx_log_stderr(0, "configuration file %s test failed",
init_cycle.conf_file.data);
}
return 1;
}
......
./core/ngx_cycle.c:ngx_init_cycle
......
//为所有模块建立一个指针数组,没个模块根据序号index对应一个指针
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
if (cycle->conf_ctx == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
......
//为核心模块(NGX_CORE_MODULE)分配结构体,具体有哪些核心模块呢?
//通过grep代码就可以知道。我们不解释了。解释关键的几个就可以说明问题了。
//第一个模块就是ngx_core_module 在nginx.c文件中。
//第二个模块是`ngx_events_module` 在ngx_event.c文件中。
//第三个模块是`ngx_http_module` 在ngx_http.c文件中。
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
//指向核心模块的上下文
module = ngx_modules[i]->ctx;
//如果上下文中create_conf函数指针不为空,就执行该指针指向的函数,分配核心模块配置结构体。
//实际上我们要说的这三个模块,只有`ngx_core_module`模块设置了函数。
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
//把分配的结构体放到数组的对应下标中。实际上该数组的类型是void,你可以放入任何类型的值。
//实际上也确实是放入不同类型的值,只不过都是指针,有的是指针的指针的指针而已。
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
看下核心模块上下文定义 core/ngx_conf_file.h
typedef 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;
上面说到只有ngx_core_module
模块设置了函数指针,那来看下该函数:
static void *
ngx_core_module_create_conf(ngx_cycle_t *cycle)
{
ngx_core_conf_t *ccf;
ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));
if (ccf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc()
*
* ccf->pid = NULL;
* ccf->oldpid = NULL;
* ccf->priority = 0;
* ccf->cpu_affinity_n = 0;
* ccf->cpu_affinity = NULL;
*/
ccf->daemon = NGX_CONF_UNSET;
ccf->master = NGX_CONF_UNSET;
ccf->timer_resolution = NGX_CONF_UNSET_MSEC;
ccf->worker_processes = NGX_CONF_UNSET;
ccf->debug_points = NGX_CONF_UNSET;
ccf->rlimit_nofile = NGX_CONF_UNSET;
ccf->rlimit_core = NGX_CONF_UNSET;
ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;
ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;
if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t))
!= NGX_OK)
{
return NULL;
}
return ccf;
}
我们回到ngx_init_cycle接着往下看。 ngx_cycle.c:ngx_init_cycle
......
ngx_memzero(&conf, sizeof(ngx_conf_t));
/* STUB: init array ? */
conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
if (conf.args == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
if (conf.temp_pool == 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;
#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;
}
......
看下ngx_conf_parse
函数的实现。该函数调用了ngx_conf_read_token
和ngx_conf_handler
。ngx_conf_read_token
看起来很复杂,其作用就是解析配置文件中的指令,把指令存入cf->args数组中,例如:usr liwq;就会解析成[usr,liwq]。cf->args->elts[0]就是指令,cf->args->elts[1...]是指令的值。感兴趣的童鞋可以自己看一下源码,在此就不说明了。重点说下ngx_conf_handler
,这才是解析配置文件核心调度函数。
为什么说是核心调度函数呢?看代码:
ngx_conf_file.c
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
char *rv;
void *conf, **confp;
ngx_uint_t i, found;
ngx_str_t *name;
ngx_command_t *cmd;
name = cf->args->elts;
found = 0;
// 遍历所有的模块
for (i = 0; ngx_modules[i]; i++) {
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;
}
found = 1;
//模块类型检查
if (ngx_modules[i]->type != NGX_CONF_MODULE
&& ngx_modules[i]->type != cf->module_type)
{
continue;
}
/* is the directive's location right ? */
//指令类型检查
if (!(cmd->type & cf->cmd_type)) {
continue;
}
if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"directive \"%s\" is not terminated by \";\"",
name->data);
return NGX_ERROR;
}
if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"directive \"%s\" has no opening \"{\"",
name->data);
return NGX_ERROR;
}
/* is the directive's argument count right ? */
//指令值的个数检查
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) {
if (cf->args->nelts < 2) {
goto invalid;
}
} else if (cmd->type & NGX_CONF_2MORE) {
if (cf->args->nelts < 3) {
goto invalid;
}
} else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {
goto invalid;
} else if (!(cmd->type & argument_number[cf->args->nelts - 1]))
{
goto invalid;
}
}
/* set up the directive's configuration context */
//根据指令的类型,选择解析出的配置文件存放在哪,其实都是存在cycle->conf_ctx指针数组中,但是存在哪个数组的那一层中是不同的。
conf = NULL;
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[ngx_modules[i]->index];
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[ngx_modules[i]->ctx_index];
}
}
//调用具体的解析指令函数
rv = cmd->set(cf, cmd, conf);
if (rv == NGX_CONF_OK) {
return NGX_OK;
}
if (rv == NGX_CONF_ERROR) {
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%s\" directive %s", name->data, rv);
return NGX_ERROR;
}
}
if (found) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%s\" directive is not allowed here", name->data);
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unknown directive \"%s\"", name->data);
return NGX_ERROR;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of arguments in \"%s\" directive",
name->data);
return NGX_ERROR;
}
配置文件解析过程有几个地方很关键
其中定义在ngx_init_cycle
函数中,为核心模块分配了结构体,在ngx_conf_handler
函数中引用的。
// ngx_init_cycle 部分代码
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
// ngx_conf_handler 部分, 其中 NGX_DIRECT_CONF flag的,都使用了上述分配的结构体。 ctx不为空的情况,需要hanlder函数内部分配结构体。无论是框架分配还是主模块分配,实际上分配的都是一个数组(有的是一个数组,例如event模块。有的是多个数组,例如stream模块和http模块),数组每一项是对应模块的配置的结构体。
conf = NULL;
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index]; // 数组
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]); // 数组
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[cf->cycle->modules[i]->ctx_index]; // 数组
}
}
rv = cmd->set(cf, cmd, conf);
例如event core模块,分配的就是一个数组,然后再遍历event所有模块
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;
if (*(void **) conf) {
return "is duplicate";
}
/* count the number of the event modules and set up their indices */
ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);
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;
}
*(void **) conf = ctx;
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
if (m->create_conf) {
(*ctx)[cf->cycle->modules[i]->ctx_index] =
m->create_conf(cf->cycle);
if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
......
例如stream core模块,分配的就是一个结构体,该结构体包含的是2个数组,最后再调用stream模块回调函数分配对应模块的配置结构体,保存到数组中。
static char *
ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_uint_t i, m, mi, s;
ngx_conf_t pcf;
ngx_array_t ports;
ngx_stream_listen_t *listen;
ngx_stream_module_t *module;
ngx_stream_conf_ctx_t *ctx;
ngx_stream_core_srv_conf_t **cscfp;
ngx_stream_core_main_conf_t *cmcf;
if (*(ngx_stream_conf_ctx_t **) conf) {
return "is duplicate";
}
/* the main stream context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*(ngx_stream_conf_ctx_t **) conf = ctx;
/* count the number of the stream modules and set up their indices */
ngx_stream_max_module = ngx_count_modules(cf->cycle, NGX_STREAM_MODULE);
/* the stream main_conf context, it's the same in the all stream contexts */
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_stream_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the stream null srv_conf context, it is used to merge
* the server{}s' srv_conf's
*/
ctx->srv_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_stream_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* create the main_conf's and the null srv_conf's of the all stream modules
*/
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
mi = cf->cycle->modules[m]->ctx_index;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
......
写个类似的c函数模拟这几种情况
int test_nginx_conf(void) {
void *conf = NULL;
void ****conf_ctx = malloc(3 * sizeof(void*));
// NGX_DIRECT_CONF
conf_ctx[0] = malloc(sizeof(int));
// use
conf = ((void **) conf_ctx)[0];
*(int*)conf = 0;
// NGX_MAIN_CONF|BLOCK
conf = &((void **)conf_ctx)[1];
void ***ctx;
ctx = malloc(sizeof(void*));
*ctx = malloc(1 * sizeof(int*));
*(void **) conf = ctx;
(*ctx)[0] = malloc(sizeof(int));
// use
void **confp;
confp = *(void **)ctx;
conf = confp[0];
*(int*)conf = 1;
return 0;
}