概述
在上一篇文章《 Nginx 启动初始化过程》简单介绍了 Nginx 启动的过程,并分析了其启动过程的源码。在启动过程中有一个步骤非常重要,就是调用函数 ngx_init_cycle(),该函数的调用为配置解析提供了接口。配置解析接口大概可分为两个阶段:准备数据阶段 和配置解析阶段;
准备数据阶段包括:
- 准备内存;
- 准备错误日志;
- 准备所需数据结构;
配置解析阶段是调用函数:
/* 配置文件解析 */
if (ngx_conf_param(&conf) != NGX_CONF_OK) {/* 带有命令行参数'-g' 加入的配置 */
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {/* 解析配置文件*/
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
配置解析
ngx_conf_t 结构体
该结构体用于 Nginx 在解析配置文件时描述每个指令的属性,也是Nginx 程序中非常重要的一个数据结构,其定义于文件:src/core/ngx_conf_file.h
/* 解析配置时所使用的结构体 */
struct ngx_conf_s {
char *name; /* 当前解析到的指令 */
ngx_array_t *args; /* 当前指令所包含的所有参数 */
ngx_cycle_t *cycle; /* 待解析的全局变量ngx_cycle_t */
ngx_pool_t *pool; /* 内存池 */
ngx_pool_t *temp_pool;/* 临时内存池,分配一些临时数组或变量 */
ngx_conf_file_t *conf_file;/* 待解析的配置文件 */
ngx_log_t *log; /* 日志信息 */
void *ctx; /* 描述指令的上下文 */
ngx_uint_t module_type;/* 当前解析的指令的模块类型 */
ngx_uint_t cmd_type; /* 当前解析的指令的指令类型 */
ngx_conf_handler_pt handler; /* 模块自定义的handler,即指令自定义的处理函数 */
char *handler_conf;/* 自定义处理函数需要的相关配置 */
};
配置文件信息 conf_file
conf_file 是存放 Nginx 配置文件的相关信息。ngx_conf_file_t 结构体的定义如下:
typedef struct {
ngx_file_t file; /* 文件的属性 */
ngx_buf_t *buffer; /* 文件的内容 */
ngx_uint_t line; /* 文件的行数 */
} ngx_conf_file_t;
配置上下文 ctx
Nginx 的配置文件是分块配置的,常见的有http 块、server 块、location 块以及upsteam 块和 mail 块等。每一个这样的配置块代表一个作用域。高一级配置块的作用域包含了多个低一级配置块的作用域,也就是有作用域嵌套的现象。这样,配置文件中的许多指令都会同时包含在多个作用域内。比如,http 块中的指令都可能同时处于http 块、server 块和location 块等三层作用域内。
在 Nginx 程序解析配置文件时,每一条指令都应该记录自己所属的作用域范围,而配置文件上下文ctx 变量就是用来存放当前指令所属的作用域的。在Nginx 配置文件的各种配置块中,http 块可以包含子配置块,这在存储结构上比较复杂。
指令类型 type
Nginx 程序中的不同的指令类型以宏的形式定义在不同的源码头文件中,指令类型是core 模块类型的定义在文件:src/core/ngx_conf_file.h
#define NGX_DIRECT_CONF 0x00010000
#define NGX_MAIN_CONF 0x01000000
#define NGX_ANY_CONF 0x0F000000
这些是
core 类型模块支持的指令类型。其中的
NGX_DIRECT_CONF类指令在
Nginx 程序进入配置解析函数之前已经初始化完成,所以在进入配置解析函数之后可以将它们直接解析并存储到实际的数据结构中,从配置文件的结构上来看,它们一般指的就是那些游离于配置块之外、处于配置文件全局块部分的指令。
NGX_MAIN_CONF 类指令包括
event、
http、
mail、
upstream 等可以形成配置块的指令,它们没有自己的初始化函数。
Nginx 程序在解析配置文件时如果遇到
NGX_MAIN_CONF 类指令,将转入对下一级指令的解析。
以下是 event 类型模块支持的指令类型。
#define NGX_EVENT_CONF 0x02000000
以下是 http 类型模块支持的指令类型,其定义在文件:src/http/ngx_http_config.h
#define NGX_HTTP_MAIN_CONF 0x02000000
#define NGX_HTTP_SRV_CONF 0x04000000
#define NGX_HTTP_LOC_CONF 0x08000000
#define NGX_HTTP_UPS_CONF 0x10000000
#define NGX_HTTP_SIF_CONF 0x20000000
#define NGX_HTTP_LIF_CONF 0x40000000
#define NGX_HTTP_LMT_CONF 0x80000000
通用模块配置解析
配置解析模块在 src/core/ngx_conf_file.c 中实现。模块提供的接口函数主要是ngx_conf_parse。另外,模块提供另一个单独的接口ngx_conf_param,用来解析命令行传递的配置,这个接口也是对ngx_conf_parse 的包装。首先看下配置解析函数 ngx_conf_parse,其定义如下:
/*
* 函数功能:配置文件解析;
* 支持三种不同的解析类型:
* 1、解析配置文件;
* 2、解析block块设置;
* 3、解析命令行配置;
*/
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
char *rv;
ngx_fd_t fd;
ngx_int_t rc;
ngx_buf_t buf;
ngx_conf_file_t *prev, conf_file;
enum {
parse_file = 0,
parse_block,
parse_param
} type;
#if (NGX_SUPPRESS_WARN)
fd = NGX_INVALID_FILE;
prev = NULL;
#endif
if (filename) {/* 若解析的是配置文件 */
/* open configuration file */
/* 打开配置文件 */
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%s\" failed",
filename->data);
return NGX_CONF_ERROR;
}
prev = cf->conf_file;
cf->conf_file = &conf_file;
if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
ngx_fd_info_n " \"%s\" failed", filename->data);
}
cf->conf_file->buffer = &buf;
buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
if (buf.start == NULL) {
goto failed;
}
buf.pos = buf.start;
buf.last = buf.start;
buf.end = buf.last + NGX_CONF_BUFFER;
buf.temporary = 1;
/* 复制文件属性及文件内容 */
cf->conf_file->file.fd = fd;
cf->conf_file->file.name.len = filename->len;
cf->conf_file->file.name.data = filename->data;
cf->conf_file->file.offset = 0;
cf->conf_file->file.log = cf->log;