nginx源码分析--配置文件详解

对 Nginx 配置文件的一些认识:

  • 配置指令具有作用域,分为全局作用域和使用 {} 创建其它作用域。
  • 同一作用域的不同的配置指令没有先后顺序;同一作用域是否能使用相同的指令,和对相 同指令的处理由各模块自行决定
  • 整个 Nginx 的运行时各模块行为都和配置指令密切相关
  • 每个配置指令都只能在预先定义好的作用域中使用
  • 配置指令解析使用递归的方式

配置解析相关代码量巨大,本文势必很冗长。所以,都要做好心理准备。同时,由于 mail 模块平时使用实在不多,故,本文只对 http 应用场景下的配置解析过程进行分析。

同时,为了行文方便,先约定一些名称:

  • 配置文件 – 指 Nginx 的主配置文件 nginx.conf。在配置文件中使用 include 引入其它配置文件的方式这里不做分析。

  • 配置指令 – 配置文件中的基本单位,配置指令可接收0个或多个配置指令参数

  • 配置项 – 配置指令对应的内部存储。一个配置指令的值可能会影响到多个配置项的 值。

  • 配置项结构体 – 每个模块相关的配置项集中管理用的结构体。

  • 作用域模块配置数组 – 每个作用域对应的一个 void * 类型的数组。每个 void * 指针指向可用于此作用域的模块对应的配置项结构体

基本概念

  • Nginx 的模块使用 ngx_module_t 结构表示

  • Nginx 按分类和等级,将模块划分为 NGX_CORE_MODULENGX_EVENT_MODULENGX_HTTP_MODULENGX_MAIL_MODULE 等类型,由 ngx_module_t.type 存储。

  • Nginx 的配置指令使用 ngx_command_t 结构进行 “声明”。模块支持的配置指令由 ngx_module_t.commands 数组存储。

  • 模块上下文 (module context) 定义了针对模块配置的一系列操作 (申请配置存储、 继承上级模块的配置等等),不同的操作在配置分析的不同阶段被调用。模块上下文根据模 块类型分为 ngx_core_module_tngx_event_module_tngx_http_module_tngx_mail_module_t 等等。模块上下文由 ngx_module_t.ctx 存储。

  • 配置指令的作用域。任何一个配置指令都有其能够使用的范围,即其作用域。配置指令如 果在其作用域外使用,Nginx 都会打印错误信息并退出。配置指令作用域目前有 NGX_MAIN_CONFNGX_EVENT_CONFNGX_HTTP_MAIN_CONFNGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONFNGX_MAIL_MAIN_CONFNGX_MAIL_SRV_CONF 等。

    • 一个配置指令可以在多个作用域中定义。
    • 在父作用域里定义的指令,也会被子作用域继承。但是,如果子作用域也使用了同一 个指令,子作用域的指令值会覆盖高级别作用域的指令。
  • 配置指令的参数。配置指令可以接受 0 个或多个参数。它能接受的参数个数也需要在定 义配置指令时明确声明,以便 Nginx 在解析配置过程中,对配置文件正确性进行检查。

    • 有一些配置指令,比如,http, event, server, location, mail, types 等,用来显式的定义作用域,它们被标识为 NGX_CONF_BLOCK,表示一个 {} 块的开始。
  • 配置指令的作用域和可接受参数等信息,由 ngx_command_t.type 字段存储。

  • 配置指令解析函数。基本每个配置指令 (简单的单值指令除外) 都定义了相应的回调函数。 Nginx 在解析配置过程中,如果碰到了该指令,会调用此回调函数对指令进行进一步的解 析。

下图是一个简单 nginx.conf 的作用域示意图:

ngx-conf-scope

  • Nginx 配置存储的内部结构,也是按作用域组织的。每个作用域都为其支持指令可以出现 在该作用域的模块预留有位置 (一个 void *)。

  • 默认配置情况下,Nginx 启用的模块对应的类型 (模块按启用顺序从上到下) 如下:

    ------------------------------------------------------
        module                          type
    ------------------------------------------------------
    ngx_core_module                 NGX_CORE_MODULE
    ngx_errlog_module               NGX_CORE_MODULE
    ngx_conf_module                     NGX_CONF_MODULE
    ngx_events_module               NGX_CORE_MODULE
    ngx_event_core_module               NGX_EVENT_MODULE
    ngx_epoll_module                    NGX_EVENT_MODULE
    ngx_http_module                 NGX_CORE_MODULE
    ngx_http_core_module                NGX_HTTP_MODULE
    ngx_http_log_module                 NGX_HTTP_MODULE
    ngx_http_upstream_module            NGX_HTTP_MODULE
    ngx_http_autoindex_module           NGX_HTTP_MODULE
    ...                                 NGX_HTTP_MODULE

准备工作

配置文件解析有两种场景:

  • Nginx 进程启动时,读取配置文件,并根据配置完成相应初始化。
  • Nginx 收到重新加载配置文件的信号后,读取配置文件,并根据当前配置文件完成相成相 应初始化。随后,释放根据老配置文件申请的资源 (被新配置重复利用的除外)。

本文也只针对第一种场景进行分析。

在配置解析开始之前,Nginx 要为配置解析过程分配一个持久内存池和临时内存池。从持久 内存池中申请的内存,用于存储从配置文件中解析完成并正确初始化的配置结构体;而临时 内存池中申请的内存,只在解析过程中作为临时存储,待配置解析完成后,就会被回收。

随后,Nginx 准备第一层级的模块配置索引数组,第一层级只存储 NGX_CORE_MODULE 模 块的配置结构体,这一层级的作用域为 NGX_MAIN_CONF

    ------------core/ngx_cycle.c:183------------------------
    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
    ...
    if (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);
            ...
            cycle->conf_ctx[ngx_modules[i]->index] = rv;
        }
    }

解析框架

配置解析流程相当简洁标准:准备当前作用域上下文,读取指令并分析指令参数,最后再调 用指令对应的回调函数。

拿第一层级 NGX_MAIN_CONF 作用域来说,其解析过程大致如下:

    ------------core/ngx_cycle.c:246------------
    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;
    ...
    ngx_conf_parse(&conf, &cycle->conf_file);

    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) {
   
            module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index]);
            ...
        }
    }

上面的代码解析 NGX_CORE_MODULE 类型的模块,解析出来的指令位于作用域 NGX_MAIN_CONF 中。同时,解析完成的数据,按模块存放于 conf.ctx 中。在配置文件 解析完毕后,Nginx 调 NGX_CORE_MODULE 类模块的 init_conf 函数根据已经读取到的 配置完成进一步初始化操作。

ngx_conf_parse 是配置解析的入口函数,它根据当成上下文读取配置文件数据,进行分 析的同时对配置文件语法进行检查。同时,这个函数会在指令处理函数修改作用域等上下文 信息后,被间接递归调用。

下面就来看一看 ngx_conf_parse 的执行流程。

    ----------core/ngx_conf_file.c:101----------------
    char *
    ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
    {
   
        ngx_buf_t buf;

        if (filename) {
   
            fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
            ...
            cf->conf_file = &conf_file;
            ...
            cf->conf_file->buffer = &buf;
            ...
            buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
            ...               
            cf->conf_file->file.fd = fd;
            ...

            type = parse_file;

        } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
   

            type = parse_block;

        } else {
   
            type = parse_param;
        ...

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

第一次调用 ngx_conf_parse 时,函数会打开配置文件,设置正在执行的解析类型,读取 token,然后调用 ngx_conf_handler。那么 ngx_conf_handler 又做了些什么呢?

    -------------core/ngx_conf_file.c:279-------------
    static ngx_int_t
    ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
    {
   
        ...       
        for (i = 0; ngx_modules[i]; i++) {
   
            /* module type checking */
            ...
            cmd = ngx_modules[i]->commands;
            ...
            for ( /* void */ ; cmd->name.len; cmd++) {
   
                /* name comparison */
                ...
                /* namespace checking */
                ...
                /* checking argument numbers */
                ...
                /* set up the directive's configuration context */

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

                } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值