本节是在配置文件解析(即
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 |