Nginx配置文件处理过程

本文详细总结了Nginx处理配置文件的主要步骤和过程,包括配置文件的初始化、模块索引顺序的设定、配置参数的解析、模块初始化函数的调用等关键环节。同时,解释了配置文件处理的主要函数及其工作原理,如ngx_init_cycle、ngx_conf_param、ngx_conf_parse等。通过解析这些核心组件,深入理解了Nginx如何实现高效的配置管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文总结这段时间阅读Nginx处理配置文件的代码。其中很多参考网上的资料,主要时淘宝的那一份。现在,算是有个大体的了解了。先记录下来,以后有加深的认识,再继续补上。加油!

一、配置文件处理的主要步骤和过程

nginx.c文件main函数

    ngx_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

初始化所有模块的索引顺序ngx_modules[i]->index,和ngx_max_module。

调用ngx_init_cycle(&init_cycle)。

ngx_init_cycle

1.创建conf_ctx存放所有配置结构体

    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));

2.NGX_CORE_MODULE模块配置结构体初始化,并存放于cycle->conf_ctx数组中。

    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;
        }
    }
3.调用ngx_conf_param()和ngx_conf_parse()解析配置文件。具体看下一个章节。

conf.ctx = cycle->conf_ctx;
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;
    }

4.调用NGX_CORE_MODULE模块的init_conf函数。

   for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;

        if (module->init_conf) {
            if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
                == NGX_CONF_ERROR)
            {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }


5.调用module的init_module函数。

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_module) {
            if (ngx_modules[i]->init_module(cycle) != NGX_OK) {
                /* fatal */
                exit(1);
            }
        }
    }

ngx_conf_parse()函数

截取主要代码部分。读取一个配置指令,交给ngx_conf_handler进行处理,如果返回NGX_ERROR则出错结束。

for ( ;; ) {
        rc = ngx_conf_read_token(cf);
        rc = ngx_conf_handler(cf, rc);

        if (rc == NGX_ERROR) {
            goto failed;
        }
    }

ngx_conf_handler函数

1. 遍历所有模块的cmd数组,比较token是否和cmd的指令名称相同。当找到一个相同的之后,进入下一步。

先看以下ngx_command_s结构体的定义。

struct ngx_command_s {
    ngx_str_t             name;   /* 指令的名称 */
    ngx_uint_t            type;   
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};


type:指令的属性集合,由多个常量值取与,主要设置指令参数 个数,指令值类型,指令出现的位置。

指令参数个数:NGX_CONF_NOARGS(不接收参数),NGX_CONF_TAKE1(一个参数),NGX_CONF_TAKE12(一个或两个参数)。

指令值类型:NGX_CONF_BLOCK(配置信息块),NGX_CONF_FLAG(接收“on"或"off")

指令出现位置:NGX_DIRECT_CONF(配置文件中最外层,如daemon,master_process等),NGX_MAIN_CONF(http, mail, events, error_log等),NGX_HTTP_MAIN_CONF(http配置指令里),NGX_HTTP_SRV_CONF(http配置指令里的server配置指令里),NGX_HTTP_LOC_CONF(http配置指令里的server配置指令里的location配置指令里),还有其他的。

set:是在解析配置文件时,遇到这个指令时被调用的函数。

conf:指定配置信息存储的内存位置。NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET, NGX_HTTP_LOC_CONF_OFFSET。(这个值在获取指令对应的conf时会被用到)。

offset:定义指令将配置的结构体的哪个成员。(后面说明如何被调用)

2. 检查当前指令是否出现在合法的位置。配置文件是嵌套解析的,当进入http的部分时,cf->cmd_type会被设置为NGX_HTTP_MAIN_CONF。所以只有那些cmd->type包含这个值的才会被处理,否则会被跳过。

            if (!(cmd->type & cf->cmd_type)) {
                continue;
            }

3.检查指令参数个数是否合理。

4.获取指令对应的conf(配置结构体,用于存放指令的设置值)。

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

对于HTTP模块,会执行第三种情况。这时cf->ctx对应的是http的配置结构体ngx_http_conf_ctx_t,包含三个数组main_conf, srv_conf, loc_conf。使用cmd->conf等到对应的数组。再通过ngx_modules[i]->ctx_index获得模块对应的conf结构体(在http指令被解析到的时候,已经通过调用模块的create_main_conf, create_srv_conf, create_loc_conf创建,并存储在对应的数组中)。

5.调用指令定义时留下的set函数。

rv = cmd->set(cf, cmd, conf);

NGX_HTTP_MODULE: ngx_http.c文件

这个文件定义了ngx_http_module模块,是属于NGX_CORE_MODULE类型的,包含“http"指令,这个指令的type是NGX_MAIN_CONF | NGX_CONF_BLOCK,对应的set是ngx_http_block。接下来,看看ngx_http_block做些什么。
1.计算NGX_HTTP_MODULE模块的索引值,以及ngx_http_max_module。
    ngx_http_max_module = 0;

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_http_max_module++;
    }

2.创建三个数组main_conf, srv_conf, loc_conf用于存放所有HTTP模块将产生的配置结构体指针。调用每个模块的create_main_conf, create_srv_conf, create_loc_conf,并将返回的配置结构体指针放在对应的数组当中。

    ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        mi = ngx_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;
            }
        }

        if (module->create_loc_conf) {
            ctx->loc_conf[mi] = module->create_loc_conf(cf);
            if (ctx->loc_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

3. 调用每个模块的preconfiguration函数进行预处理。

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->preconfiguration) {
            if (module->preconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }
4. 进入http配置指令的内部指令解析过程,包括server配置指令结构,location配置指令结构等。

    pcf = *cf;   /* pcf保存原配置信息 */
    cf->ctx = ctx;
    cf->module_type = NGX_HTTP_MODULE;   
    cf->cmd_type = NGX_HTTP_MAIN_CONF;   /* 改变配置文件解析位置信息 */
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf; /* 最后在返回前,恢复 */

5. init http{} main_conf's, merge the server{}s' srv_conf's and its location{}s' loc_conf's 和create location trees。这部分还没看懂。

6.init_phases

7.调用HTTP模块的postconfiguration函数,做后期处理。例如,默认值处理可以放在这里执行。

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

8. ngx_http_variables_init_vars

9.ngx_http_init_phase_handlers

10.ngx_http_optimize_servers(),调用ngx_http_init_listening -> 调用ngx_http_add_listening:设置ls->handler = ngx_http_init_connection,其中ls为ngx_listening_t结构体。


NGX_EVENT_MODULE: ngx_events.c文件

当遇到'events'指令时,将调用ngx_events_block()函数。执行以下内容:

1. 计算NGX_EVENT_MODULE的数量

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


2.创建指针数组存放NGX_EVENT_MODULE模块的conf结构体。

    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;


3. 调用create_conf函数

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

4.进入events配置模块,进行解析。

    pcf = *cf;
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

5.调用init_conf函数

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

二、问题解析

ngx_command_s结构体中offset如何被使用?

char *
ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_str_t        *field, *value;
    ngx_conf_post_t  *post;

    field = (ngx_str_t *) (p + cmd->offset); /* 使用offset获取结构体成员地址指针 */

    if (field->data) {
        return "is duplicate";
    }

    value = cf->args->elts;

    *field = value[1];

    if (cmd->post) {
        post = cmd->post;
        return post->post_handler(cf, post, field);
    }

    return NGX_CONF_OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值