本节是在配置文件解析(即
ngx_cycle_t ngx_init_cycle(ngx_cycle_t *old_cycle))函数上的深入学习,主要围绕ngx_module_t类型数组,ngx_command_t类型数组与void *ctx上下文结构体进行。
学习目标
- 了解ngx_module_t结构设计,nginx的模块化设计就是基于这个结构,不仅在配置文件解析,在很多模块的设计都离不开该结构,需要在这里有个基础的认识。以后再学习其他模块时,在加深对其的了解。
 - 学习nginx模块解析流程,同时也是对上一章Nginx源码分析-从编译到启动的补充。
 
ngx_module_t结构
从上一章Nginx源码分析-从编译到启动可知,configure脚本执行后,产生了ngx_module[]数组(/objs/ngx_modules.c):
1 | 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, | 
10 |       &ngx_stream_write_filter_module, | 
11 |       &ngx_stream_return_module, | 
12 |       // ... | 
13 |       NULL | 
14 | }; | 
该数组的类型为ngx_module_s类型,该类型定义在/src/core/ngx_module.h文件中:
1 | /* 该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 | |
10 |     char                 *name; | 
11 |     /* 保留字段,暂未使用 */ | 
12 |     ngx_uint_t            spare0; | 
13 |     ngx_uint_t            spare1; | 
14 | |
15 |     ngx_uint_t            version; | 
16 |     const char           *signature; | 
17 |     void                 *ctx;          /* ctx用于指向一类模块的上下文结构 */ | 
18 |     ngx_command_t        *commands;     /* 用于处理nginx.conf中的配置项 */ | 
19 |     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 |  | 
42 |     NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \ | 
43 |     NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE | 
44 | |
45 |  | 
先看一下这个结构体是如何声明的, 以下是ngx_events_module,ngx_core_module与ngx_http_log_module结构体声明:
1 | 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 */ | 
10 |     NULL,                                  /* exit thread */ | 
11 |     NULL,                                  /* exit process */ | 
12 |     NULL,                                  /* exit master */ | 
13 |     NGX_MODULE_V1_PADDING | 
14 | }; | 
15 | |
16 | ngx_module_t  ngx_core_module = { | 
17 |       NGX_MODULE_V1, | 
18 |       &ngx_core_module_ctx,   /* module context */ | 
19 |       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_V1和NGX_MODULE_V1_PADDING都是用来快速填充的宏,其中index,ctx_index在声明时都是NGX_MODULE_UNSET_INDEX默认值,它们的初始化在它们即将使用之前,例如:index的初始化在main()函数启动时执行,对ngx_module[]数组按顺序进行编号。
1 | 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 | |
10 |     ngx_modules_n = i; | 
11 |     ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES; | 
12 | |
13 |     return NGX_OK; | 
14 | } | 
ngx_modeule_t结构体中最核心的关注点是void *ctx,ngx_command_t commands和nginx_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。每种类型有不同的ctx和commands。
模块类型如下图所示,每类模块都有一个代表模块(核心模块),ctx_index即表示该模块在该类模块中的位置与优先级。

ctx用于指向一类模块的上下文结构,由于nginx各模块执行的功能各不相同, ctx承载其不同的特性,所有设置为void *型。本节关注配置文件解析的ngx_core_module_ctx功能,其他模块的ctx功能本节暂时不关注。
看一些具体的例子: ngx_events_module和ngx_core_module的type是NGX_CORE_MODULE类型,对应的ctx都是ngx_core_module_t类型的,ngx_http_log_module类型的type是NGX_HTTP_MODULE类型,对应的ctx是ngx_http_module_t类型的。
1 | 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, | 
10 |     ngx_event_init_conf | 
11 | }; | 
12 | |
13 | static ngx_core_module_t  ngx_core_module_ctx = { | 
14 |     ngx_string("core"), | 
15 |     ngx_core_module_create_conf, | 
16 |     ngx_core_module_init_conf | 
17 | }; | 
18 | |
19 | 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行为对应的状态。这里看一下该结构的定义与使用例子,下一节看下他在解析过程中的调用与使用。
1 | 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方法处理配置项的参数 */ | 
10 |     char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); | 
11 |     /* crate分配内存的时候的偏移量NGX_HTTP_LOC_CONF_OFFSET/NGX_HTTP_SRV_CONF_OFFSET */ | 
12 |     ngx_uint_t            conf; | 
13 |     /*  通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计。 | 
14 |      *  它需要与conf配合使用 | 
15 |      *  */ | 
16 |     ngx_uint_t            offset; | 
17 |     /* 如果使用Nginx预设的配置项解析方法,就需要根据这些预设方法来决定post的使用方式 */ | 
18 |     void                 *post; | 
19 | }; | 
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,其对应的ctx和commands_s成员为:ngx_core_module_ctx和ngx_core_commands。
1 | 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 */ | 
10 |       NULL,                   /* exit thread */ | 
11 |       NULL,                   /* exit process */ | 
12 |       NULL,                   /* exit master */ | 
13 |       NGX_MODULE_V1_PADDING | 
14 | }; | 
1 | 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; | 
1 | 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文件里。
1 | 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里进行默认初始化。
1 | 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模块 */ | 
10 |        } | 
11 |        module = cycle->modules[i]->ctx; | 
12 |        if (module->create_conf) { | 
13 |            rv = module->create_conf(cycle); | 
14 |            cycle->conf_ctx[cycle->modules[i]->index] = rv; | 
15 |        } | 
16 |     } | 
17 |     /* 其它... */ | 
18 |     conf.ctx = cycle->conf_ctx; | 
19 |     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函数,一般是在处理一些特殊配置指令或复杂配置项,比如指令include、events、http、server、location等的处理时。
解析过程大致分为三步:
- 设置函数当前的解析状态。
 ngx_conf_read_token()读取部分配置文件进行词法分析获取token。- 读取适当token之后对其进行实际处理,
ngx_conf_handler()根据token行为设置nginx内对应的变量。 
ngx_conf_parse存在三种解析状态:
- 刚开始进行配置文件解析的
parse_file状态。 - 开始解析一个配置块的
parse_block状态。 - 对应
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中的基础数据结构,不止用于该缓冲区与内存数据块,目前不展开讲该结构,本节只注重配置文件解析。
1 | 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 |      * 含义相同 */ | 
10 |     off_t            file_pos; | 
11 |     off_t            file_last; | 
12 |     /* 如果ngx_buf_t缓冲区用于内存,则start表示内存起始地址 | 
13 |      * end指向内存末尾地址 */ | 
14 |     u_char          *start;         /* start of buffer */ | 
15 |     u_char          *end;           /* end of buffer */ | 
16 |     /* 表示当前缓冲区类型,例如由哪个模块使用就指向 | 
17 |      * 这个模块ngx_module_t变量的地址 */ | 
18 |     ngx_buf_tag_t    tag; | 
19 |     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反复使用该缓存区。
1 | 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 | |
10 |     if (filename) { | 
11 |         /* open configuration file */ | 
12 |         fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); | 
13 |         /* 其它... */ | 
14 |         /* buffer缓冲区用于后续配置文件解析时读取到内存中的配置信息存储区域 */ | 
15 |         cf->conf_file->buffer = &buf; | 
16 | |
17 |         buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); | 
18 |         if (buf.start == NULL) { | 
19 |             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中。比如:
daemon on;解析后对应的cf->args保存["daemon", "on"]events {解析后对应的cf->args保存["events"]
1 | 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; | 
10 |     ngx_buf_t   *b, *dump;  | 
11 | |
12 |     found = 0;      /* 标志位,表示找到一个token */ | 
13 | |
14 |     /* 一些标志位 */ | 
15 |     need_space = 0; | 
16 |     last_space = 1; | 
17 |     sharp_comment = 0; | 
18 |     variable = 0; | 
19 |     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", | 
100 |                                    n, size); | 
101 |                 return NGX_ERROR; | 
102 |             } | 
103 | |
104 |             b->pos = b->start + len; | 
105 |             b->last = b->pos + n; | 
106 |             start = b->start; | 
107 | |
108 |             if (dump) { | 
109 |                 dump->last = ngx_cpymem(dump->last, b->pos, size); | 
110 |             } | 
111 |         } | 
112 | |
113 |         ch = *b->pos++;             /* 逐一扫描字符 */ | 
114 | |
115 |         if (ch == LF) {             /* 换行符 */ | 
116 |             cf->conf_file->line++; | 
117 | |
118 |             if (sharp_comment) { | 
119 |                 sharp_comment = 0; | 
120 |             } | 
121 |         } | 
122 | |
123 |         if (sharp_comment) {        /* 注释行不处理 */ | 
124 |             continue; | 
125 |         } | 
126 | |
127 |         if (quoted) { | 
128 |             quoted = 0; | 
129 |             continue; | 
130 |         } | 
131 | |
132 |         if (need_space) { | 
133 |             if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { | 
134 |                 last_space = 1; | 
135 |                 need_space = 0; | 
136 |                 continue; | 
137 |             } | 
138 | |
139 |             if (ch == ';') { | 
140 |                 return NGX_OK; | 
141 |             } | 
142 | |
143 |             if (ch == '{') {        /* '{'表示是一个复杂配置项的开始 */ | 
144 |                 return NGX_CONF_BLOCK_START; | 
145 |             } | 
146 | |
147 |             if (ch == ')') { | 
148 |                 last_space = 1; | 
149 |                 need_space = 0; | 
150 | |
151 |             } else { | 
152 |                  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 
153 |                                     "unexpected \"%c\"", ch); | 
154 |                  return NGX_ERROR; | 
155 |             } | 
156 |         } | 
157 | |
158 |         if (last_space) {           /* 如果上一个字符是空格 */ | 
159 |             if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { | 
160 |                 continue; | 
161 |             } | 
162 | |
163 |             start = b->pos - 1; | 
164 |             start_line = cf->conf_file->line; | 
165 | |
166 |             switch (ch) { | 
167 | |
168 |             case ';': | 
169 |             case '{': | 
170 |                 if (cf->args->nelts == 0) { | 
171 |                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 
172 |                                        "unexpected \"%c\"", ch); | 
173 |                     return NGX_ERROR; | 
174 |                 } | 
175 | |
176 |                 if (ch == '{') { | 
177 |                     return NGX_CONF_BLOCK_START; | 
178 |                 } | 
179 | |
180 |                 return NGX_OK; | 
181 | |
182 |             case '}': | 
183 |                 if (cf->args->nelts != 0) { | 
184 |                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 
185 |                                        "unexpected \"}\""); | 
186 |                     return NGX_ERROR; | 
187 |                 } | 
188 | |
189 |                 return NGX_CONF_BLOCK_DONE; | 
190 | |
191 |             case '#': | 
192 |                 sharp_comment = 1; | 
193 |                 continue; | 
194 | |
195 |             case '\\': | 
196 |                 quoted = 1; | 
197 |                 last_space = 0; | 
198 |                 continue; | 
199 | |
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 | 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 | |
10 |     found = 0; | 
11 | |
12 |     for (i = 0; ngx_modules[i]; i++) { | 
13 |         cmd = ngx_modules[i]->commands; | 
14 |         if (cmd == NULL) { | 
15 |             continue; | 
16 |         } | 
17 | |
18 |         for ( /* void */ ; cmd->name.len; cmd++) { | 
19 | |
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 | |
100 |                 if (confp) {  | 
101 |                     conf = confp[ngx_modules[i]->ctx_index];  | 
102 |                 } | 
103 |             } | 
104 | |
105 |             rv = cmd->set(cf, cmd, conf); | 
106 |             /* 其它... */ | 
107 | } | 
这里有个复杂设计,对应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{}内部的命令。 
1 | 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]; | 
10 |     } | 
11 | } | 
set函数
set函数就是ngx_comand[]数组里对应数组元素的char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);成员回调函数。
1 | 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 | |
10 |     { ngx_string("master_process"), | 
11 |       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG, | 
12 |       ngx_conf_set_flag_slot, | 
13 |       0, | 
14 |       offsetof(ngx_core_conf_t, master), | 
15 |       NULL }, | 
16 | |
17 |     { ngx_string("timer_resolution"), | 
18 |       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, | 
19 |       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 }, | 
100 | |
101 |     { ngx_string("env"), | 
102 |       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, | 
103 |       ngx_set_env, | 
104 |       0, | 
105 |       0, | 
106 |       NULL }, | 
107 | |
108 |     { ngx_string("load_module"), | 
109 |       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, | 
110 |       ngx_load_module, | 
111 |       0, | 
112 |       0, | 
113 |       NULL }, | 
114 | |
115 |       ngx_null_command | 
116 | }; | 
117 | |
118 | ngx_module_t  ngx_core_module = { | 
119 |     NGX_MODULE_V1, | 
120 |     &ngx_core_module_ctx,                  /* module context */ | 
121 |     ngx_core_commands,                     /* module directives */ | 
122 |     NGX_CORE_MODULE,                       /* module type */ | 
123 |     NULL,                                  /* init master */ | 
124 |     NULL,                                  /* init module */ | 
125 |     NULL,                                  /* init process */ | 
126 |     NULL,                                  /* init thread */ | 
127 |     NULL,                                  /* exit thread */ | 
128 |     NULL,                                  /* exit process */ | 
129 |     NULL,                                  /* exit master */ | 
130 |     NGX_MODULE_V1_PADDING | 
131 | }; | 
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 |