7

Nginx源码分析-配置文件解析

 2 years ago
source link: https://atticuslab.com/2020/09/29/nginx-annotated-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本节是在配置文件解析(即ngx_cycle_t ngx_init_cycle(ngx_cycle_t *old_cycle))函数上的深入学习,主要围绕ngx_module_t类型数组,ngx_command_t类型数组与void *ctx上下文结构体进行。

  1. 了解ngx_module_t结构设计,nginx的模块化设计就是基于这个结构,不仅在配置文件解析,在很多模块的设计都离不开该结构,需要在这里有个基础的认识。以后再学习其他模块时,在加深对其的了解。
  2. 学习nginx模块解析流程,同时也是对上一章Nginx源码分析-从编译到启动的补充。

ngx_module_t结构

从上一章Nginx源码分析-从编译到启动可知,configure脚本执行后,产生了ngx_module[]数组(/objs/ngx_modules.c):

ngx_module_t *ngx_modules[] = {
2
      &ngx_core_module,
3
      &ngx_errlog_module,
4
      &ngx_conf_module,
5
      &ngx_events_module,
6
      &ngx_event_core_module,
7
      &ngx_kqueue_module,
8
      &ngx_stream_module,
9
      &ngx_stream_core_module,
      &ngx_stream_write_filter_module,
      &ngx_stream_return_module,
      // ...
      NULL
};

该数组的类型为ngx_module_s类型,该类型定义在/src/core/ngx_module.h文件中:

/* 该typedef在src/core/ngx_core.h */
2
typedef struct ngx_module_s ngx_module_t
3
4
struct ngx_module_s {
5
    /* 该模块在该类模块中的序号,该类模块类型由type指定 */
6
    ngx_uint_t            ctx_index;
7
    /* 该模块在ngx_modules[]数组中的编号 */
8
    ngx_uint_t            index;
9
    char                 *name;
    /* 保留字段,暂未使用 */
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            version;
    const char           *signature;
    void                 *ctx;          /* ctx用于指向一类模块的上下文结构 */
    ngx_command_t        *commands;     /* 用于处理nginx.conf中的配置项 */
    ngx_uint_t            type;         /* 模块类型,即该类模块所属的类型 */
20
    /* 在nginx启动停止过程中,一下7个函数指针表示:
21
     * 有7个执行点会分别调用这7种方法,如果不需要可设置为NULL */
22
    ngx_int_t           (*init_master)(ngx_log_t *log);
23
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);
24
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
25
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
26
    void                (*exit_thread)(ngx_cycle_t *cycle);
27
    void                (*exit_process)(ngx_cycle_t *cycle);
28
    void                (*exit_master)(ngx_cycle_t *cycle);
29
    /* 保留字段,当前没有被使用 */
30
    uintptr_t             spare_hook0;
31
    uintptr_t             spare_hook1;
32
    uintptr_t             spare_hook2;
33
    uintptr_t             spare_hook3;
34
    uintptr_t             spare_hook4;
35
    uintptr_t             spare_hook5;
36
    uintptr_t             spare_hook6;
37
    uintptr_t             spare_hook7;
38
};
39
40
/* 该类型还定义了两个宏,来初始化一些通用成员 */
41
#define NGX_MODULE_V1                                                         \
42
    NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \
43
    NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE
44
45
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

先看一下这个结构体是如何声明的, 以下是ngx_events_modulengx_core_modulengx_http_log_module结构体声明:

ngx_module_t  ngx_events_module = {
2
    NGX_MODULE_V1,
3
    &ngx_events_module_ctx,                /* module context */
4
    ngx_events_commands,                   /* module directives */
5
    NGX_CORE_MODULE,                       /* module type */
6
    NULL,                                  /* init master */
7
    NULL,                                  /* init module */
8
    NULL,                                  /* init process */
9
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
ngx_module_t  ngx_core_module = {
      NGX_MODULE_V1,
      &ngx_core_module_ctx,   /* module context */
      ngx_core_commands,      /* module directives */
20
      NGX_CORE_MODULE,        /* module type */
21
      NULL,                   /* init master */
22
      NULL,                   /* init module */
23
      NULL,                   /* init process */
24
      NULL,                   /* init thread */
25
      NULL,                   /* exit thread */
26
      NULL,                   /* exit process */
27
      NULL,                   /* exit master */
28
      NGX_MODULE_V1_PADDING
29
};
30
31
ngx_module_t  ngx_http_log_module = {
32
    NGX_MODULE_V1,
33
    &ngx_http_log_module_ctx,              /* module context */
34
    ngx_http_log_commands,                 /* module directives */
35
    NGX_HTTP_MODULE,                       /* module type */
36
    NULL,                                  /* init master */
37
    NULL,                                  /* init module */
38
    NULL,                                  /* init process */
39
    NULL,                                  /* init thread */
40
    NULL,                                  /* exit thread */
41
    NULL,                                  /* exit process */
42
    NULL,                                  /* exit master */
43
    NGX_MODULE_V1_PADDING
44
};

由上可知NGX_MODULE_V1NGX_MODULE_V1_PADDING都是用来快速填充的宏,其中index,ctx_index在声明时都是NGX_MODULE_UNSET_INDEX默认值,它们的初始化在它们即将使用之前,例如:index的初始化在main()函数启动时执行,对ngx_module[]数组按顺序进行编号。

ngx_int_t ngx_preinit_modules(void)
2
{
3
    ngx_uint_t  i;
4
5
    for (i = 0; ngx_modules[i]; i++) {
6
        ngx_modules[i]->index = i;
7
        ngx_modules[i]->name = ngx_module_names[i];
8
    }
9
    ngx_modules_n = i;
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;
    return NGX_OK;
}

ngx_modeule_t结构体中最核心的关注点是void *ctxngx_command_t commandsnginx_unit_t type这三个结构。

type与ctx结构

其中ngx_uint_t type表示模块的类型,它与ctx指针密切相关, 其取值范围为:NGX_HTTP_MODULE, NGX_CORE_MODULE, NGX_CONF_MODULE, NGX_EVENT_MODULE, NGX_MAIL_MODULE。每种类型有不同的ctxcommands

模块类型如下图所示,每类模块都有一个代表模块(核心模块),ctx_index即表示该模块在该类模块中的位置与优先级。

ctx用于指向一类模块的上下文结构,由于nginx各模块执行的功能各不相同, ctx承载其不同的特性,所有设置为void *型。本节关注配置文件解析的ngx_core_module_ctx功能,其他模块的ctx功能本节暂时不关注。

看一些具体的例子: ngx_events_modulengx_core_moduletypeNGX_CORE_MODULE类型,对应的ctx都是ngx_core_module_t类型的,ngx_http_log_module类型的typeNGX_HTTP_MODULE类型,对应的ctxngx_http_module_t类型的。

typedef struct {
2
    ngx_str_t             name;
3
    void               *(*create_conf)(ngx_cycle_t *cycle);
4
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
5
} ngx_core_module_t;
6
7
static ngx_core_module_t  ngx_events_module_ctx = {
8
    ngx_string("events"),
9
    NULL,
    ngx_event_init_conf
};
static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};
typedef struct {
20
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
21
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
22
23
    void       *(*create_main_conf)(ngx_conf_t *cf);
24
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);
25
26
    void       *(*create_srv_conf)(ngx_conf_t *cf);
27
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
28
29
    void       *(*create_loc_conf)(ngx_conf_t *cf);
30
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
31
} ngx_http_module_t;
32
33
34
static ngx_http_module_t  ngx_http_log_module_ctx = {
35
    NULL,                                  /* preconfiguration */
36
    ngx_http_log_init,                     /* postconfiguration */
37
38
    ngx_http_log_create_main_conf,         /* create main configuration */
39
    NULL,                                  /* init main configuration */
40
41
    NULL,                                  /* create server configuration */
42
    NULL,                                  /* merge server configuration */
43
44
    ngx_http_log_create_loc_conf,          /* create location configuration */
45
    ngx_http_log_merge_loc_conf            /* merge location configuration */
46
};

ngx_command_t结构

ngx_command_s类型用于处理nginx.conf配置项,该类型一般以数组的形式使用,每个数组元素都是该类型的元素,数组的结尾用ngx_null_command表示,nginx解析出配置文件nginx.conf中的配置项(例如:daemon on)后,会遍历commands数组,匹配到对应name等于dameon的数组元素后,将对应的nginx内置变量至于on行为对应的状态。这里看一下该结构的定义与使用例子,下一节看下他在解析过程中的调用与使用。

struct ngx_command_s {
2
    ngx_str_t             name;     /* 配置项名称 */
3
    /* 配置项类型,type将指定配置项可以出现的位置。例如,出现在server{ }或
4
     * location{ }中,以及它可以携带的参数个数。
5
     * type可以同时取多个值,各值之间用|符号连接,例如,type可以取值为
6
     * NGX_TTP_MAIN_CONF | NGX_HTTP_SRV_CONFI | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE。
7
     * */
8
    ngx_uint_t            type;
9
    /* 出现了name中指定的配置项后,讲调用set方法处理配置项的参数 */
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    /* crate分配内存的时候的偏移量NGX_HTTP_LOC_CONF_OFFSET/NGX_HTTP_SRV_CONF_OFFSET */
    ngx_uint_t            conf;
    /*  通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计。
     *  它需要与conf配合使用
     *  */
    ngx_uint_t            offset;
    /* 如果使用Nginx预设的配置项解析方法,就需要根据这些预设方法来决定post的使用方式 */
    void                 *post;
};
20
21
static ngx_command_t  ngx_events_commands[] = {
22
23
    { ngx_string("events"),
24
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
25
      ngx_events_block,
26
      0,
27
      0,
28
      NULL },
29
30
      ngx_null_command
31
};
32
33
static ngx_command_t  ngx_core_commands[] = {
34
35
    { ngx_string("daemon"),
36
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
37
      ngx_conf_set_flag_slot,
38
      0,
39
      offsetof(ngx_core_conf_t, daemon),
40
      NULL },
41
42
    { ngx_string("master_process"),
43
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
44
      ngx_conf_set_flag_slot,
45
      0,
46
      offsetof(ngx_core_conf_t, master),
47
      NULL },
48
    //.省略跟多项.......
49
    { ngx_string("env"),
50
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
51
      ngx_set_env,
52
      0,
53
      0,
54
      NULL },
55
56
    { ngx_string("load_module"),
57
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
58
      ngx_load_module,
59
      0,
60
      0,
61
      NULL },
62
63
      ngx_null_command
64
};
65
66
static ngx_command_t  ngx_http_log_commands[] = {
67
68
    { ngx_string("log_format"),
69
      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
70
      ngx_http_log_set_format,
71
      NGX_HTTP_MAIN_CONF_OFFSET,
72
      0,
73
      NULL },
74
75
    { ngx_string("access_log"),
76
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
77
                        |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
78
      ngx_http_log_set_log,
79
      NGX_HTTP_LOC_CONF_OFFSET,
80
      0,
81
      NULL },
82
83
    { ngx_string("open_log_file_cache"),
84
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
85
      ngx_http_log_open_file_cache,
86
      NGX_HTTP_LOC_CONF_OFFSET,
87
      0,
88
      NULL },
89
90
      ngx_null_command
91
};

nginx配置文件解析

上一节中我们看了ngx_module_t结构体,其中有三个关注点:ctx,commands_t,types。其中与配置文件解析相关的types类型为NGX_CORE_MODULE,其对应的ctxcommands_s成员为:ngx_core_module_ctxngx_core_commands

ngx_module_t  ngx_core_module = {
2
      NGX_MODULE_V1,
3
      &ngx_core_module_ctx,   /* module context */
4
      ngx_core_commands,      /* module directives */
5
      NGX_CORE_MODULE,        /* module type */
6
      NULL,                   /* init master */
7
      NULL,                   /* init module */
8
      NULL,                   /* init process */
9
      NULL,                   /* init thread */
      NULL,                   /* exit thread */
      NULL,                   /* exit process */
      NULL,                   /* exit master */
      NGX_MODULE_V1_PADDING
};
typedef struct {
2
    ngx_str_t             name;
3
    void               *(*create_conf)(ngx_cycle_t *cycle);
4
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
5
} ngx_core_module_t;
static ngx_core_module_t  ngx_core_module_ctx = {
2
    ngx_string("core"),
3
    ngx_core_module_create_conf,
4
    ngx_core_module_init_conf
5
};

main()函数相关解析流程

当nginx启动时,main()函数里与处理模块有关的只有下边这部分,ngx_preinit_modules()在上边已经讲了,主要用来初始化ngx_modules[]->index的模块编号,设置模块名称。该模块来自configure脚本产生的objs/ngx_modules.c文件里。

int main(int argc, char *const *argv) {
2
    /* 其它... */
3
    ngx_cycle_t      *cycle, init_cycle;
4
    /* 其它... */
5
    if (ngx_preinit_modules() != NGX_OK) { return 1; }
6
7
    cycle = ngx_init_cycle(&init_cycle);
8
    /* 其它... */
9
}

配置文件解析开始于main函数里的ngx_init_cycle()函数。这里只对核心模块进行处理,调用核心函数模块的ctx结构(即ngx_core_module_ctx)成员回调函数create_conf,为配置信息分配内存空间,并且对一些配置变量进行NGX_CONF_UNSET的初始化。之后在ngx_conf_parse()函数里进行配置文件解析,如果一些必要设置没有在nginx.conf文件里进行设置的话,后续会在init_conf里进行默认初始化。

ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) {
2
    /* 其它... */
3
    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
4
    /* 其它... */
5
    if (ngx_cycle_modules(cycle) != NGX_OK) { /* ... */ }
6
    /* 其它... */
7
    for (i = 0; cycle->modules[i]; i++) {
8
       if (cycle->modules[i]->type != NGX_CORE_MODULE) { 
9
            continue;   /* 只处理ngx_core_module模块 */
       }
       module = cycle->modules[i]->ctx;
       if (module->create_conf) {
           rv = module->create_conf(cycle);
           cycle->conf_ctx[cycle->modules[i]->index] = rv;
       }
    }
    /* 其它... */
    conf.ctx = cycle->conf_ctx;
    conf.cycle = cycle;
20
    conf.module_type = NGX_CORE_MODULE;
21
    conf.cmd_type = NGX_MAIN_CONF;
22
    /* 其它... */
23
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { /* ... */ } 
24
    /* 其它... */
25
    for (i = 0; cycle->modules[i]; i++) {
26
        /* 其它... */
27
        if (module->init_conf) {
28
            if (module->init_conf(cycle, cycle->conf_ctx[cycle->modules[i] \ 
29
                ->index]) == NGX_CONF_ERROR) 
30
            { /*..*/ }
31
        }
32
    }
33
    /* 其它... */
34
}

ngx_conf_parse函数

ngx_conf_parse函数开始真正的配置文件解析。它是一个间接的递归函数,执行到的一些函数(比如ngx_conf_handler)内又会调用ngx_conf_parse函数,一般是在处理一些特殊配置指令或复杂配置项,比如指令includeeventshttpserverlocation等的处理时。

解析过程大致分为三步:

  1. 设置函数当前的解析状态。
  2. ngx_conf_read_token()读取部分配置文件进行词法分析获取token。
  3. 读取适当token之后对其进行实际处理,ngx_conf_handler()根据token行为设置nginx内对应的变量。

ngx_conf_parse存在三种解析状态:

  1. 刚开始进行配置文件解析的parse_file状态。
  2. 开始解析一个配置块的parse_block状态。
  3. 对应ngx_conf_parm()解析命令行参数的配置信息时的parse_param状态。

ngx_conf_read_token()函数并不会频繁的去读取配置文件,它每次从文件内读取足够多的内容以填满一个大小为NGX_CONF_BUFFER的缓存区(除最后一次,即配置文件剩余内容本来就不够了),该缓冲区类型为typedef struct ngx_buf_s ngx_buf_t;

ngx_buf_t配置读取缓冲区

ngx_buf_t是nginx处理大块数据的结构,是nginx中的基础数据结构,不止用于该缓冲区与内存数据块,目前不展开讲该结构,本节只注重配置文件解析。

typedef struct ngx_buf_s  ngx_buf_t;
2
3
struct ngx_buf_s {
4
    /* pos通常是用来告诉使用者本次应该从pos这个位置开始处理内存中的
5
     * 数据,因为ngx_buf_t可能被多次反复处理。 */
6
    u_char          *pos;
7
    u_char          *last;          /* 表示有效内存到此为止 */
8
    /* 处理文件时,file_pos与file_last的含义与处理内存时的post与last的
9
     * 含义相同 */
    off_t            file_pos;
    off_t            file_last;
    /* 如果ngx_buf_t缓冲区用于内存,则start表示内存起始地址
     * end指向内存末尾地址 */
    u_char          *start;         /* start of buffer */
    u_char          *end;           /* end of buffer */
    /* 表示当前缓冲区类型,例如由哪个模块使用就指向
     * 这个模块ngx_module_t变量的地址 */
    ngx_buf_tag_t    tag;
    ngx_file_t      *file;          /* 引用的文件 */
20
    ngx_buf_t       *shadow;        /* 影子缓冲区,当前不知道哪里用 */
21
22
    /* 位域 */
23
    /* the buf's content could be changed */
24
    unsigned         temporary:1;   /* 标志位,为1时表示数据在内存中且可修改 */
25
26
    /*
27
     * the buf's content is in a memory cache or in a read only memory
28
     * and must not be changed
29
     */
30
    unsigned         memory:1;      /* 为1时,表示这段内存不可修改 */
31
32
    /* the buf's content is mmap()ed and must not be changed */
33
    unsigned         mmap:1;        /* 为1时表示这段内存mmap映射而来,不可被修改 */
34
35
    unsigned         recycled:1;    /* 为1表示可回收 */
36
    unsigned         in_file:1;     /* 为1表示是文件而不是内存 */
37
    unsigned         flush:1;       /* 为1时表示需要指向flush操作 */
38
    unsigned         sync:1;        /*  */
39
    unsigned         last_buf:1;    /* 是否是最后一块缓冲区 */
40
    unsigned         last_in_chain:1;   /* 是否是当前最后一块待处理缓冲区 */
41
42
    unsigned         last_shadow:1; /*  */
43
    unsigned         temp_file:1;   /* 表示当前缓冲区是否是临时文件 */
44
45
    /* STUB */ int   num;
46
};

这个缓存区在函数ngx_conf_parse()内申请并保存引用到变量cf->conf_file->buffer内,函数ngx_conf_read_token反复使用该缓存区。

char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
2
{
3
    /* 其它... */
4
    enum {  /* 解析状态标记 */
5
        parse_file = 0,
6
        parse_block,
7
        parse_param
8
    } type;
9
    if (filename) {
        /* open configuration file */
        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
        /* 其它... */
        /* buffer缓冲区用于后续配置文件解析时读取到内存中的配置信息存储区域 */
        cf->conf_file->buffer = &buf;
        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
        if (buf.start == NULL) {
            goto failed;
20
        }
21
        /* pos缓冲区已扫描的位置 */
22
        buf.pos = buf.start;    /* 缓冲区开始位置 */
23
        buf.last = buf.start;   /* 读取缓冲区的结束位置,可能读不满整个缓冲区 */
24
        buf.end = buf.last + NGX_CONF_BUFFER;   /* 缓冲区最大大小的结束位置 */
25
        buf.temporary = 1; /* 临时内存标志位,为1时表示数据在内存中且可被修改 */
26
        /* 其它... */
27
        type = parse_file;
28
        /* 其它... */
29
    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
30
        type = parse_block;
31
    } else {
32
        type = parse_param;
33
    }
34
35
    for ( ;; ) {
36
        /* 读取配置文件,逐个字符扫描,进行词法分析,解析单个token */
37
        rc = ngx_conf_read_token(cf);
38
        /* 其它... */
39
        /* 对每个配置项进行具体的处理 */
40
        rc = ngx_conf_handler(cf, rc);
41
        /* 其它... */
42
    }
43
/* 其它... */
44
}

ngx_conf_read_token函数

ngx_conf_read_token()函数基于状态机解析配置文件,识别配指令和参数, 每次扫描一个token就会存入cf->args中。比如:

  1. daemon on; 解析后对应的cf->args保存["daemon", "on"]
  2. events { 解析后对应的cf->args保存["events"]
static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf)
2
{
3
    u_char      *start, ch, *src, *dst;
4
    off_t        file_size;
5
    size_t       len;
6
    ssize_t      n, size;
7
    ngx_uint_t   found, need_space, last_space, sharp_comment, variable;
8
    ngx_uint_t   quoted, s_quoted, d_quoted, start_line;
9
    ngx_str_t   *word;
    ngx_buf_t   *b, *dump; 
    found = 0;      /* 标志位,表示找到一个token */
    /* 一些标志位 */
    need_space = 0;
    last_space = 1;
    sharp_comment = 0;
    variable = 0;
    quoted = 0;
20
    s_quoted = 0;
21
    d_quoted = 0;
22
23
    cf->args->nelts = 0;
24
    b = cf->conf_file->buffer;
25
    dump = cf->conf_file->dump;
26
    start = b->pos;
27
    start_line = cf->conf_file->line;
28
29
    file_size = ngx_file_size(&cf->conf_file->file.info);
30
31
    for ( ;; ) {
32
33
        if (b->pos >= b->last) {        /* 当buf内的数据全部扫描后 */
34
35
            if (cf->conf_file->file.offset >= file_size) {
36
37
                if (cf->args->nelts > 0 || !last_space) {
38
39
                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
40
                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
41
                                           "unexpected end of parameter, "
42
                                           "expecting \";\"");
43
                        return NGX_ERROR;
44
                    }
45
46
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
47
                                  "unexpected end of file, "
48
                                  "expecting \";\" or \"}\"");
49
                    return NGX_ERROR;
50
                }
51
52
                return NGX_CONF_FILE_DONE;  /* 配置文件读取完毕 */
53
            }
54
55
            len = b->pos - start;           /* 已扫描的长度 */
56
57
            if (len == NGX_CONF_BUFFER) {   /* 已扫描全部buf */
58
                cf->conf_file->line = start_line;
59
60
                if (d_quoted) {             /* 缺少右双引号 */
61
                    ch = '"';
62
63
                } else if (s_quoted) {      /* 缺少右单引号 */
64
                    ch = '\'';
65
66
                } else { 
67
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
68
                                       "too long parameter \"%*s...\" started",
69
                                       10, start);
70
                    return NGX_ERROR;
71
                }
72
73
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
74
                                   "too long parameter, probably "
75
                                   "missing terminating \"%c\" character", ch);
76
                return NGX_ERROR;
77
            }
78
79
            if (len) {
80
                ngx_memmove(b->start, start, len);
81
            }
82
            /* 配置文件未读入的长度 */
83
            size = (ssize_t) (file_size - cf->conf_file->file.offset);
84
            /* 修正可读入的配置文件长度 */
85
            if (size > b->end - (b->start + len)) {
86
                size = b->end - (b->start + len);
87
            }
88
89
            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
90
                              cf->conf_file->file.offset);
91
92
            if (n == NGX_ERROR) {
93
                return NGX_ERROR;
94
            }
95
96
            if (n != size) {
97
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
98
                                   ngx_read_file_n " returned "
99
                                   "only %z bytes instead of %z",
                                   n, size);
                return NGX_ERROR;
            }
            b->pos = b->start + len;
            b->last = b->pos + n;
            start = b->start;
            if (dump) {
                dump->last = ngx_cpymem(dump->last, b->pos, size);
            }
        }
        ch = *b->pos++;             /* 逐一扫描字符 */
        if (ch == LF) {             /* 换行符 */
            cf->conf_file->line++;
            if (sharp_comment) {
                sharp_comment = 0;
            }
        }
        if (sharp_comment) {        /* 注释行不处理 */
            continue;
        }
        if (quoted) {
            quoted = 0;
            continue;
        }
        if (need_space) {
            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
                last_space = 1;
                need_space = 0;
                continue;
            }
            if (ch == ';') {
                return NGX_OK;
            }
            if (ch == '{') {        /* '{'表示是一个复杂配置项的开始 */
                return NGX_CONF_BLOCK_START;
            }
            if (ch == ')') {
                last_space = 1;
                need_space = 0;
            } else {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "unexpected \"%c\"", ch);
                 return NGX_ERROR;
            }
        }
        if (last_space) {           /* 如果上一个字符是空格 */
            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
                continue;
            }
            start = b->pos - 1;
            start_line = cf->conf_file->line;
            switch (ch) {
            case ';':
            case '{':
                if (cf->args->nelts == 0) {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "unexpected \"%c\"", ch);
                    return NGX_ERROR;
                }
                if (ch == '{') {
                    return NGX_CONF_BLOCK_START;
                }
                return NGX_OK;
            case '}':
                if (cf->args->nelts != 0) {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "unexpected \"}\"");
                    return NGX_ERROR;
                }
                return NGX_CONF_BLOCK_DONE;
            case '#':
                sharp_comment = 1;
                continue;
            case '\\':
                quoted = 1;
                last_space = 0;
                continue;
200
            case '"':
201
                start++;
202
                d_quoted = 1;
203
                last_space = 0;
204
                continue;
205
206
            case '\'':
207
                start++;
208
                s_quoted = 1;
209
                last_space = 0;
210
                continue;
211
212
            default:
213
                last_space = 0;
214
            }
215
216
        } else {
217
            if (ch == '{' && variable) {
218
                continue;
219
            }
220
221
            variable = 0;
222
223
            if (ch == '\\') {
224
                quoted = 1;
225
                continue;
226
            }
227
228
            if (ch == '$') {        /* 变量标志位 */
229
                variable = 1;
230
                continue;
231
            }
232
233
            if (d_quoted) {
234
                if (ch == '"') {    /* 找到一对双引号 */
235
                    d_quoted = 0;
236
                    need_space = 1;
237
                    found = 1;
238
                }
239
240
            } else if (s_quoted) {  /* 找到一对单引号 */
241
                if (ch == '\'') {
242
                    s_quoted = 0;
243
                    need_space = 1;
244
                    found = 1;
245
                }
246
247
            } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
248
                       || ch == ';' || ch == '{')
249
            {
250
                last_space = 1;
251
                found = 1;
252
            }
253
254
            if (found) {            /* 找到一个配置项 */
255
                word = ngx_array_push(cf->args);
256
                if (word == NULL) {
257
                    return NGX_ERROR;
258
                }
259
260
                word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);
261
                if (word->data == NULL) {
262
                    return NGX_ERROR;
263
                }
264
265
                for (dst = word->data, src = start, len = 0;
266
                     src < b->pos - 1;
267
                     len++)
268
                {
269
                    if (*src == '\\') {     /* 转义字符 */
270
                        switch (src[1]) {
271
                        case '"':
272
                        case '\'':
273
                        case '\\':
274
                            src++;
275
                            break;
276
277
                        case 't':
278
                            *dst++ = '\t';
279
                            src += 2;
280
                            continue;
281
282
                        case 'r':
283
                            *dst++ = '\r';
284
                            src += 2;
285
                            continue;
286
287
                        case 'n':
288
                            *dst++ = '\n';
289
                            src += 2;
290
                            continue;
291
                        }
292
293
                    }
294
                    *dst++ = *src++;
295
                }
296
                *dst = '\0';
297
                word->len = len;
298
299
                if (ch == ';') {            /* 分号,表示简单配置项解析完成 */
300
                    return NGX_OK;
301
                }
302
303
                if (ch == '{') {            /* 复杂配置项解析开始 */
304
                    return NGX_CONF_BLOCK_START;
305
                }
306
307
                found = 0;
308
            }
309
        }
310
    }
311
}

ngx_conf_handler函数

ngx_command_s类型数组就是在该函数ngx_conf_handler(cf,rc)内部使用。该函数在ngx_command[]数组内寻找在ngx_conf_read_token()解析出来的配置项,并调用其set函数。

找到一个符号的配置项有一下条件决定:

  1. 名字一致, 配置文件中指令的名字和模块指令中的名字需要一致。
  2. 模块类型一致, 配置文件指令处理的模块类型和当前模块一致。
  3. 指令类型一致, 配置文件指令类型和当前模块指令一致。
  4. 参数个数一致, 配置文件中参数的个数和当前模块的当前指令参数一致。
static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
2
{
3
   /* 其它... */
4
   for(ia = 0; ia < cf->args->nelts; ia++) {
5
        memset(tmp, 0, sizeof(tmp));
6
        snprintf(tmp, sizeof(tmp), "%s ", name[ia].data);
7
        strcat(buf, tmp);
8
    }
9
    found = 0;
    for (i = 0; ngx_modules[i]; i++) {
        cmd = ngx_modules[i]->commands;
        if (cmd == NULL) {
            continue;
        }
        for ( /* void */ ; cmd->name.len; cmd++) {
20
            if (name->len != cmd->name.len) {
21
                continue;
22
            }
23
24
            if (ngx_strcmp(name->data, cmd->name.data) != 0) {
25
                continue;
26
            }
27
28
            found = 1;
29
30
            if (ngx_modules[i]->type != NGX_CONF_MODULE
31
                && ngx_modules[i]->type != cf->module_type)
32
            {
33
                continue;
34
            }
35
36
            /* is the directive's location right ? */
37
38
            if (!(cmd->type & cf->cmd_type)) {
39
                continue;
40
            }
41
42
            if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
43
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
44
                                  "directive \"%s\" is not terminated by \";\"",
45
                                  name->data);
46
                return NGX_ERROR;
47
            }
48
49
            if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
50
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
51
                                   "directive \"%s\" has no opening \"{\"",
52
                                   name->data);
53
                return NGX_ERROR;
54
            }
55
56
            /* is the directive's argument count right ? */
57
58
            if (!(cmd->type & NGX_CONF_ANY)) {
59
60
                if (cmd->type & NGX_CONF_FLAG) {
61
62
                    if (cf->args->nelts != 2) {
63
                        goto invalid;
64
                    }
65
66
                } else if (cmd->type & NGX_CONF_1MORE) {
67
68
                    if (cf->args->nelts < 2) {
69
                        goto invalid;
70
                    }
71
72
                } else if (cmd->type & NGX_CONF_2MORE) {
73
74
                    if (cf->args->nelts < 3) {
75
                        goto invalid;
76
                    }
77
78
                } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {
79
80
                    goto invalid;
81
82
                } else if (!(cmd->type & argument_number[cf->args->nelts - 1]))
83
                {
84
                    goto invalid;
85
                }
86
            }
87
88
            /* set up the directive's configuration context */
89
90
            conf = NULL;
91
92
93
            if (cmd->type & NGX_DIRECT_CONF) {
94
                conf = ((void **) cf->ctx)[ngx_modules[i]->index]; 
95
            } else if (cmd->type & NGX_MAIN_CONF) { 
96
                conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); 
97
            } else if (cf->ctx) {
98
                confp = *(void **) ((char *) cf->ctx + cmd->conf);   
99
                if (confp) { 
                    conf = confp[ngx_modules[i]->ctx_index]; 
                }
            }
            rv = cmd->set(cf, cmd, conf);
            /* 其它... */
}

这里有个复杂设计,对应ngx_cycle_s->conf_ctx这个四级指针,这里先有个大致印象,后续还会再见。

  • 第一个if中执行的命令主要为NGX_DIRECT_CONF类型:ngx_core_commands,ngx_openssl_commands, ngx_google_perftools_commands, ngx_regex_commands,ngx_thread_pool_commands
  • 第二个if中执行的命令主要为NGX_MAIN_CONF:http, events, include等。
  • 第三个if中执行的命令主要是(其他)模块:http{}, events{}, server, server{} location及其location{}内部的命令。
if (cmd->type & NGX_DIRECT_CONF) {
2
    conf = ((void **) cf->ctx)[ngx_modules[i]->index];
3
} else if (cmd->type & NGX_MAIN_CONF) {
4
    conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
5
} else if (cf->ctx) {
6
    confp = *(void **) ((char *) cf->ctx + cmd->conf);
7
8
    if (confp) {
9
        conf = confp[ngx_modules[i]->ctx_index];
    }
}

set函数

set函数就是ngx_comand[]数组里对应数组元素的char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);成员回调函数。

static ngx_command_t  ngx_core_commands[] = {
2
3
    { ngx_string("daemon"),
4
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
5
      ngx_conf_set_flag_slot,
6
      0,
7
      offsetof(ngx_core_conf_t, daemon),
8
      NULL },
9
    { ngx_string("master_process"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_core_conf_t, master),
      NULL },
    { ngx_string("timer_resolution"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
20
      0,
21
      offsetof(ngx_core_conf_t, timer_resolution),
22
      NULL },
23
24
    { ngx_string("pid"),
25
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
26
      ngx_conf_set_str_slot,
27
      0,
28
      offsetof(ngx_core_conf_t, pid),
29
      NULL },
30
31
    { ngx_string("lock_file"),
32
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
33
      ngx_conf_set_str_slot,
34
      0,
35
      offsetof(ngx_core_conf_t, lock_file),
36
      NULL },
37
38
    { ngx_string("worker_processes"),
39
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
40
      ngx_set_worker_processes,
41
      0,
42
      0,
43
      NULL },
44
45
    { ngx_string("debug_points"),
46
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
47
      ngx_conf_set_enum_slot,
48
      0,
49
      offsetof(ngx_core_conf_t, debug_points),
50
      &ngx_debug_points },
51
52
    { ngx_string("user"),
53
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12,
54
      ngx_set_user,
55
      0,
56
      0,
57
      NULL },
58
59
    { ngx_string("worker_priority"),
60
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
61
      ngx_set_priority,
62
      0,
63
      0,
64
      NULL },
65
66
    { ngx_string("worker_cpu_affinity"),
67
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
68
      ngx_set_cpu_affinity,
69
      0,
70
      0,
71
      NULL },
72
73
    { ngx_string("worker_rlimit_nofile"),
74
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
75
      ngx_conf_set_num_slot,
76
      0,
77
      offsetof(ngx_core_conf_t, rlimit_nofile),
78
      NULL },
79
80
    { ngx_string("worker_rlimit_core"),
81
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
82
      ngx_conf_set_off_slot,
83
      0,
84
      offsetof(ngx_core_conf_t, rlimit_core),
85
      NULL },
86
87
    { ngx_string("worker_shutdown_timeout"),
88
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
89
      ngx_conf_set_msec_slot,
90
      0,
91
      offsetof(ngx_core_conf_t, shutdown_timeout),
92
      NULL },
93
94
    { ngx_string("working_directory"),
95
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
96
      ngx_conf_set_str_slot,
97
      0,
98
      offsetof(ngx_core_conf_t, working_directory),
99
      NULL },
    { ngx_string("env"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_set_env,
      0,
      0,
      NULL },
    { ngx_string("load_module"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_load_module,
      0,
      0,
      NULL },
      ngx_null_command
};
ngx_module_t  ngx_core_module = {
    NGX_MODULE_V1,
    &ngx_core_module_ctx,                  /* module context */
    ngx_core_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

ngx_command_t类型的type定义在core/ngx_conf_file.h文件里,预设了14个配置项解析方法,在core/ngx_conf_file.c文件里。

type类型 type取值 说明 处理配置项时获取当前配置块的方式 NGX_DIRECT_CONF

NGX_ANY_CONF 目前未使用,设置与否均无意义 配置项可以在那些{}配置块中出现 NGX_MAIN_CONF 配置项可以出现在全局配置中,即不属于任何{}配置块

NGX_EVENT_CONF 配置项可以出现在events{}块内

NGX_MAIL_MAIN_CONF 配置项可以出现在mail{}块或者imap{}块内

NGX_MAIL_SRV_CONF 配置项可以出现在server{}块内,然而该server{}块必须属于mail{}块或者imap{}块

NGX_HTTP_MAIN_CONF 配置项可以出现在http{}块内

NGX_HTTP_SRV_CONF 配置项可以出现在server{}块内,然而该server块必须属于http{}块

NGX_HTTP_LOC_CONF 配置项可以出现在location{}块内,然而该location块必须属于http{}块

NGX_HTTP_UPS_CONF 配置项可以出现在upstream{}块内,然而该upstream块必须属于http{}块

NGX_HTTP_SIF_CONF 配置项可以出现在server块内的if{}块中。目前仅有rewrite模块会使用,该if块必须属于http{}块

NGX_HTTP_LIF_CONF 配置项可以出现在location块内的if{)块中。目前仅有rewrite模块会使用,该if块必须属于http{}块

NGX_HTTP_LMT_CONF 配置项可以出现在limit_except{}块内,然而该limit-except块必须属于http{}块 限制配置项的数目 NGX_CONF_NOARGS 配置项不携带任何参数

NGX_CONF_TAKE1 配置项必须携带1个参数

NGX_CONF_TAKE2 配置项必须携带2个参数

NGX_CONF_TAKE3 配置项必须携带3个参数

NGX_CONF_TAKE4 配置项必须携带4个参数

NGX_CONF_TAKE5 配置项必须携带5个参数

NGX_CONF_TAKE6 配置项必须携带6个参数

NGX_CONF_TAKE7 配置项必须携带7个参数

NGX_CONF_TAKE12 配置项可以携带1个参数或2个参数

NGX_CONF_TAKE13 配置项可以携带1个参数或3个参数

NGX_CONF_TAKE23 配置项可以携带2个参数或3个参数

NGX_CONF_TAKE123 配置项可以携带1~3个参数

NGX_CONF_TAKE1234 配置项可以携带1~4个参数 限制配置项后的参数出现的形式 NGX_CONF_ARGS_NUMBER 目前未使用,无意义

NGX_CONF_BLOCK 配置项定义了一种新的{}块。例如,http、server、location等配置,它们的type都必须定义为NGX_CONF_BLOCK

NGX_CONF_ANY 不验证配置项携带的参数个数

NGX_CONF_FLAG 配置项携带的参数只能是1个,并且参数的值只能是on或者off

NGX_CONF_1MORE 配置项携带的参数个数必须超过1个

NGX_CONF_2MORE 配置项携带的参数个数必须超过2个

NGX_CONF_MULTI 目前,还没有官方模块使用过,暂时略过 预设方法名 说明 ngx_conf_set_flag_slot 如果nginx.conf文件中某个配置项的参数是on或者off(即希望配置项表达打开或者关闭某个功能的意思),而且在Nginx模块的代码中使用ngx_flag_t变量来保存这个配置项的参数,就可以将set回调方法设为ngx_conf_set_flag_slot。当nginx.conf文件中参数是on时,代码中的ngx_flag_t类型变量将设为1,参数为off时则设为0 ngx_conf_set_str_slot 如果配置项后只有1个参数,同时在代码中我们希望用ngx_str_t类型的变量来保存这个配置项的参数,则可以使用ngx_conf_set_str_slot方法 ngx_conf_set_str_array_slot 如果这个配置项会出现多次,每个配置项后面都跟着1个参数,而在程序中我们希望仅用一个ngx_array_t动态数组来存储所有的参数,且数组中的每个参数都以ngx_str_t来存储,那么预设的ngx_conf_set_str_array_slot有法可以帮我们做到 ngx_conf_set_keyval_slot

ngx_conf_set_num_slot 配置项后必须携带1个参数,且只能是数字。存储这个参数的变量必须是整型 ngx_conf_set_size_slot

ngx_conf_set_off_slot

ngx_conf_set_msec_slot

ngx_conf_set_sec_slot

ngx_conf_set_bufs_slot

ngx_conf_set_enum_slot

ngx_conf_set_bitmask_slot

ngx_conf_set_access_slot

ngx_conf_set_path_slot


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK