注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

偶有所得,记录在此

有分享交流才有进步,永远不要固步自封

 
 
 

日志

 
 

[Nginx 源码分析] Nginx 配置文件解析  

2011-02-14 17:58:41|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
内部数据结构
-------------------
先看下下面这个数据结构:
typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t;

这里面有三个指针数组;数组的长度相同,都是 Nginx 加载的模块的总数;三个数组相同位置上的元素的类型相同,如果 main_conf[3] 是 ngx_http_gzip_conf_t 类型的数据的话,srv_conf 和 loc_conf 也是。

以 Gzip Filter Module 为例:
static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {
    ngx_http_gzip_add_variables,           /* preconfiguration */
    ngx_http_gzip_filter_init,             /* postconfiguration */

    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */

    ngx_http_gzip_create_conf,             /* create location configuration */
    ngx_http_gzip_merge_conf               /* merge location configuration */
};
loc_conf[n] 里面的内容,就是 ngx_http_gzip_create_conf 的返回值,也就是创建并经过初始化的ngx_http_gzip_conf_t 类型的结构体数据。

没有定义创建 main_conf 和 srv_conf 的函数,所以 main_conf[n] 和 srv_conf[n] 为 NULL。


配置分三个级别:http,server,location,每一级别的每一个对象都会有一个 ngx_http_conf_ctx_t 结构来存储各个模块在各个级别的配置信息。 

http block 中 ctx 的初始化
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    // ...
    /* 初始化 http block 的 ctx*/
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(ngx_http_conf_ctx_t **) conf = ctx;


    /* count the number of the http modules and set up their indices */
    // 原英文注释够清楚了
    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++;
    }
    /*
     * the http null srv_conf context, it is used to merge
     * the server{}s' srv_conf's
     */
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }
    /*
     * the http 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_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }
    /*
     * the http null loc_conf context, it is used to merge
     * the server{}s' loc_conf's
     */

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }
   
    /*
     * create the main_conf's, the null srv_conf's, and the null loc_conf's
     * of the all http modules
     */

    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;
            }
        }
    }
    // ...
}
代码很简单,就不解释了

server block 配置的初始化是在 ngx_http_core_server 里面,和 http block 类似;
server block 只初始化 srv_conf 和 loc_conf,无 main_conf,它的 main_conf 指向 http 级别的main_conf。

location block 配置的初始化是在 ngx_http_core_location 里面,main_conf 和 srv_conf 都指向 server 级别的配置,只负责初始化 loc_conf。

配置文件解析过程
---------------------------
ngx_init_cycle  函数里面,先调用 ngx_conf_param 处理命令行的配置选项(惯例,命令行和配置文件都支持),然后执行 ngx_conf_parse(&conf, &cycle->conf_file) 进行配置文件的解析。

基本处理流程:
1)  先设置要解析的配置文件的类型 module_type 和 cmd_type,ngx_init_cycle 里面的初始化是这样的:
conf.module_type = NGX_CORE_MODULE;
conf.cmd_type = NGX_MAIN_CONF;

那问题来了,那些是 NGX_CORE_MODULE 呢?http、events、mail、error_log、ngx_core_module 等都是 NGX_CORE_MODULE。

而 http、mail、event 等 module 还包含了多个子 module。

http 的子 module 的类型是 NGX_HTTP_MODULE;event 的子 module 是 NGX_EVENT_MODULE。

解析是先从 NGX_CORE_MODULE 类型的 module 开始的。

2) 解析过程,ngx_init_cycle 里面从解析 NGX_CORE_MODULE 类型的配置开始,一行一行的解析文件。
对于每一行,执行以空格(或者 \t 等) 为分隔符字符串 split,split 的结果存储在一个数组里面;
数组的第一个元素就是配置指令;遍历所有这种类型的 module,查找该指令出现在那个模块,然后执行该模块为该指令定义的回调函数来设置相关参数。

对于含有子 module 的模块,通常会在指令的回调函数里面,重新设置 module_type 和 cmd_type ,再次执行 ngx_conf_parse 来解析所有的子模块的配置,止到无子模块为止。

相关源码解析
------------
ngx_conf_parse 函数
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{

   //...
   if (filename) {
      // 第一次调用时,会传入配置文件路径,这里打开配置文件,设置相应 buffer,和 文件的 offset 等
      // 以后调用,该参数保持为空即可
      //...
    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {

        // 基本上都是 parse block       
        type = parse_block;

    } else {
        // 这个用来解析命令行参数
        type = parse_param;
    }
    for ( ;; ) {
        // 读取一行,然后以空格或者 tab 进行 split,各个元素以数组形式存储在 cf->args 成员里面
        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
         */
         //...
        
         // 寻找该行配置指令的回调函数,然后执行设置相关参数,下面详细分析
         rc = ngx_conf_handler(cf, rc);
         /..
    }
    //...
}

ngx_conf_handler 函数
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
    //...
    name = cf->args->elts;
   
    // 开始遍历所有 module
    for (i = 0; ngx_modules[i]; i++) {

        /* look up the directive in the appropriate modules */
        if (ngx_modules[i]->type != NGX_CONF_MODULE
            && ngx_modules[i]->type != cf->module_type)
        {
            continue;
        }

        // 获取该 module 定义的所有 commands
        cmd = ngx_modules[i]->commands;
        if (cmd == NULL) {
            continue;
        }

        // 搜索所有 commands
        for ( /* void */ ; cmd->name.len; cmd++) {

            if (name->len != cmd->name.len) {
                continue;
            }

            if (ngx_strcmp(name->data, cmd->name.data) != 0) {
                continue;
            }


            // 查找成功,下面对该指令的类型、参数做检查
           
            /* is the directive's location right ? */

            if (!(cmd->type & cf->cmd_type)) {
                if (cmd->type & NGX_CONF_MULTI) {
                    multi = 1;
                    continue;
                }

                goto not_allowed;
            }

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

            /* set up the directive's configuration context */

            conf = NULL;

            // 获取相关的 ctx,这是一个配置的指针数组,前面有讲
            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);
            // ...
        }
    }

对于 http block,它执行的回调函数是 ngx_http_block,里面会继续对所有子 http module 进行解析,相关代码如下:
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    //...
    /* parse inside the http{} block */

    // 设置要解析的 module 类型为 NGX_HTTP_MODULE
    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;
   
    // 开始解析子 module
    rv = ngx_conf_parse(cf, NULL);
    /...
}
 
----
感谢代码发芽网提供的语法高亮服务。

  评论这张
 
阅读(2947)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017