在上代码之前先做点铺垫,做下简单解释,这样更方便理解源码
在调用ngx_conf_handler前,已经调用了ngx_conf_read_token将配置项信息放入到了cf->args中了,所以能从里面拿到配置项的名称和值
函数逻辑很简单,分为以下几步:
- cf->args找到配置项名字
- 然后遍历模块,每个模块继续遍历其配置项,找到模块指定配置项指针(ngx_command_t *commands)
- 调用其set(函数);
遍历过程对模块不太熟悉的可以看看
重点描述下ngx_command_s 的type,也就是配置类型,借用《深入理解nginx模块开发与架构解析》的图
下面是源码(带注释)
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;
//cf->args->elts 不是包含了配置项的名字和值吗?为什么可以取到名字?
//因为cf->args->elts是一个链表,首节点就是配置项名字,并且每个元素都以'\0'结尾了
name = cf->args->elts;
found = 0;
//遍历模块
for (i = 0; cf->cycle->modules[i]; i++) {
cmd = cf->cycle->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 (cf->cycle->modules[i]->type != NGX_CONF_MODULE
&& cf->cycle->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 */
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);
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;
}
上诉源码内容有注释,不做多解释,下面代码可能看起来可能会有点晕,我描述下自己的理解,
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);
cf->ctx 是一个void****指针,就是全局变量cycle的指针
struct ngx_cycle_s {
void ****conf_ctx;
....
}
根据配置项类型,来取出其在conf_ctx结构体中的位置
1、当类型是包含NGX_DIRECT_CONF时,"{}"外的配置,conf =((void **) cf->ctx)[cf->cycle->modules[i]->index];
2、当类型是包含NGX_MAIN_CONF时, "{}"外的配置,conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
有什么区别呢?你可以把((void *) cf->ctx)[cf->cycle->modules[i]->index]当做下图标红的void,区别如下图
3、当类型不是上诉2种呢?"{}"内的配置,这里的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);
顾名思义应该是用来设置配置结构体的,是否真是这样,举例说明:
1、ngx_core_module,下图右边红框标示就是其set函数,由于其类型是NGX_DIRECT_CONF,是核心模块,配置结构体一开始就已经分配好了,也就是conf 所指向的结构体,所以只需要设置其配置结构体的值就好了
2、ngx_http_module,其类型是NGX_MAIN_CONF,其set接口是 ngx_http_block
详情可参考ngx_http_block
在该函数内部为conf 分配了内存,代码如下:
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
...
ngx_http_conf_ctx_t *ctx;
...
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
...
*(void **) conf = ctx;
...
cf->ctx = ctx;
}
为了让void*指向ngx_http_conf_ctx_t结构体,conf取数只能如下形式
conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
3、ngx_http_core_module,既不是NGX_MAIN_CONF 也不是NGX_DIRECT_CONF,set 接口的内容也是设置值了,不过多描述
这里说下conf 的取值,为什么要先取confp
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[cf->cycle->modules[i]->ctx_index];
}
这里要注意的是此时cf->ctx在 上诉第2个过程已经更改值了,直接指向了ngx_http_conf_ctx_t结构体如下图所示
在看下ngx_http_conf_ctx_t定义,cmd->conf就是结构体成员所在的位置,根据confp = *(void **) ((char *) cf->ctx + cmd->conf);
就能知道是哪一个结构体成员,然后根据ctx_index(同级子模块所在的索引)找到指定配置结构体
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;