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

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
#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结构体声明:

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_V1NGX_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 *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类型的。

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,其对应的ctxcommands_s成员为:ngx_core_module_ctxngx_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函数,一般是在处理一些特殊配置指令或复杂配置项,比如指令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中的基础数据结构,不止用于该缓冲区与内存数据块,目前不展开讲该结构,本节只注重配置文件解析。

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中。比如:

  1. daemon on; 解析后对应的cf->args保存["daemon", "on"]
  2. 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. 名字一致, 配置文件中指令的名字和模块指令中的名字需要一致。
  2. 模块类型一致, 配置文件指令处理的模块类型和当前模块一致。
  3. 指令类型一致, 配置文件指令类型和当前模块指令一致。
  4. 参数个数一致, 配置文件中参数的个数和当前模块的当前指令参数一致。
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