源文件路径
src\core\ngx_conf_file.h
src\core\ngx_conf_file.c
主要内容
本篇的主要目的在于分析Nginx
的配置功能。由于Nginx
的配置基本就是对模块的配置,因此,在讨论配置功能之前,需要先分析Nginx
的模块功能。
对于模块功能,这里的重点不在于某个模块的细节,而在于分析Nginx
的模块架构是如何设计的。
类似地,因为对于Nginx
的配置文件,没有资料比官方文档上说得更明白,所以,这里的重点并不是对配置文件,而是分析Nginx
的配置功能是如何设计的。
好了,开始吧。
模块功能
Nginx
的基础架构是高度模块化的。可以从多个方面来观察Nginx
的模块化。
官方文档首页的整个下半页是Nginx
自身所有模块的列表。从这里可以看到Nginx
的模块轮廓。
如果从源码安装Nginx
,那么首先执行的configure
脚本的配置参数里,很大一部分是用来管理是否安装某个模块的。当然,Nginx
运行必备的模块是必须安装的,因此,并不会出现在configure
的配置选项里。
Nginx
本身由多个基本的模块构成,其中,核心的部分是一个叫ngx_core_module
的模块。当然,对于一个web服务器,仅仅有一个核心是不够的,还需要大量的“辅助模块”。这有点像Linux
的设计,一堆外围设施作为模块与Linux
内核构成整个Linux
系统。
很自然的会想到,Nginx
的这种模块化设计是支持第三方插件的。这种设计也大大增加了Nginx
的弹性和能力。
模块的组织和管理
既然Nginx
是由许多模块构成的,那么,如何组织和管理这些模块是首先要关注的问题。在Nginx
中,使用全局数组ngx_modules
保存和管理所有Nginx
的模块。
如何做到的呢?
首先,Nginx
的众多模块被分成两类:必须安装的模块和可以安装的模块。
-
必须安装的模块是保证Nginx正常功能的模块,没得选择,这些模块会出现在
ngx_modules
里。比如ngx_core_module
-
可以安装的模块通过
configure
的配置和系统环境,被有选择的安装,这些模块里,被选择安装的模块会出现在ngx_modules
数组中。
当configure
执行结束后,会生成一个objs\ngx_modules.c
的源代码文件:
文件内容随configure
配置不同而不同,例如:
#include <ngx_config.h>
#include <ngx_core.h>
extern ngx_module_t ngx_core_module;
extern ngx_module_t ngx_errlog_module;
extern ngx_module_t ngx_conf_module;
extern ngx_module_t ngx_events_module;
extern ngx_module_t ngx_event_core_module;
extern ngx_module_t ngx_epoll_module;
extern ngx_module_t ngx_regex_module;
extern ngx_module_t ngx_http_module;
extern ngx_module_t ngx_http_core_module;
extern ngx_module_t ngx_http_log_module;
extern ngx_module_t ngx_http_upstream_module;
extern ngx_module_t ngx_http_static_module;
extern ngx_module_t ngx_http_autoindex_module;
extern ngx_module_t ngx_http_index_module;
...
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
&ngx_regex_module,
&ngx_http_module,
&ngx_http_core_module,
&ngx_http_log_module,
&ngx_http_upstream_module,
&ngx_http_static_module,
&ngx_http_autoindex_module,
&ngx_http_index_module,
...
NULL
};
由于数组存放的是同一类型的数据。因此,必须有一个模块基类来抽象的定义所有模块。这个抽象基类就是ngx_module_t
。
因此,可以看到ngx_modules
数组里存放的是ngx_module_t *
类型指针,并且,最后一个一定是空指针NULL
。
将最后一个元素置为空指针NULL
,是为了遍历数组的时候,标记数组结束,这样不必在使用时记录数组大小。
例如:
for (i = 0; ngx_modules[i]; i++) {
...
}
使用extern ngx_module_t ngx_core_module;
使编译器在编译的时候不去处理ngx_core_module
,在链接阶段,再将ngx_core_module
链接到正确的地址中。
类似地,在源文件core\ngx_conf_file.h
的末尾声明外部ngx_modules[]
extern ngx_module_t *ngx_modules[];
由于core\ngx_conf_file.h
会被当作公共头文件被包含到其他文件中,因此,ngx_modules
是一个全局变量。
======分割线=======
在编译阶段,objs\ngx_modules.c
文件中的那一堆extern变量及ngx_modules
变量都会被初始化。原因在于,这些变量都属于全局变量,将会被静态初始化。
======分割线=======
这样,有了全局数组ngx_modules
,就可以获取所有模块的指针,从而操作所有模块。对所有模块的管理和操作也由此展开。
所有模块的基类 ngx_module_t
所有模块都是ngx_module_t
类型的变量,其定义如下:
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
ngx_uint_t version;
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
各成员变量含义如下:
1. type
type
变量是指模块的类型。Nginx
中模块的类型定义如下:
#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */
#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */
#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */
#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */
#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */
也就是说,Nginx
共有5中模块类型CORF
、CONF
、EVNT
、HTTP
、MAIL
。
2. index
index
变量是指,该模块在ngx_modules
数组中的次序,或者说下标。该变量在Nginx
执行初始化的时候被初始化,初始化代码位于core\nginx.c
的main
函数中:
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
3. ctx_index
ctx_index
是指,在ngx_modules
数组中,该模块在相同类型的模块中的次序。
例如,ngx_modules
数组中有多个类型为NGX_EVENT_MODULE
的模块,那么其中一个模块的ctx_index
初始化如下:
// 源码文件event\ngx_event.c
ngx_event_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
ngx_modules[i]->ctx_index = ngx_event_max_module++;
}
4. ctx
ctx
是void *
指针型变量,这是指向与模块相关的上下文。
这里先解释一下什么叫与模块相关的上下文。
上下文这个概念一般可以理解为运行环境,那么与模块相关的上下文就是指模块运行环境。换句话说,就是一些参数的配置信息。因为配置一般意味着设置,而设置的对象就是模块的运行环境。比如,ngx_core_modules
运行时的user
和group
,就属于运行环境,同时,也属于配置项。
由于模块的参数一般比较多,正确设置参数的配置需要很多操作。所以,上下文其实也意味着一组操作,这组操作的目的在于根据配置,正确设置模块的参数信息。同时,这也意味着,不同的模块,这组操作是不同的,因此,这里需要将ctx
设置为void *
指针,来进行泛型的操作。
以ngx_core_module
为例,其ctx
指向的上下文是这个样子的:
static ngx_core_module_t ngx_core_module_ctx = {
ngx_string("core"),
ngx_core_module_create_conf,
ngx_core_module_init_conf
};
其中ngx_core_module_t
的定义如下:
typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
而这组操作中的ngx_core_module_init_conf
函数中,初始化了一个ngx_core_conf_t
的结构体。
因此,可知,void *ctx
指向了一组操作,这组操作的目的在于初始化一个与模块配置信息相关的结构体。
这里涉及到了三个结构体:
-
ngx_module_t ngx_core_module
:用来表示模块本身,保存在ngx_modules
数组中; -
ngx_core_conf_t core_conf
:用来保存对该模块的配置信息; -
ngx_core_module_t ngx_core_module_ctx
:用来初始化ngx_core_conf_t
中的成员变量;
6. commands
commands是ngx_command_t
数组,表示一组配置文件中的可配项(指令)。
例如,在配置文件nginx.conf
中worker_processes 1;
对应commands
数组中的一项,该项的定义如下,类型为ngx_command_t
:
{ ngx_string("worker_processes"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_set_worker_processes,
0,
0,
NULL }
其中ngx_command_t
定义如下:
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
所以,commands
的含义在于该模块的配置项在配置文件中的指令。
ngx_module_t 总结
到这里,一个完整的模块所需的各个部分逐渐显露出来了:
-
ngx_module_t
结构体,用来表示模块的定义,被放入ngx_modules
数组中。 -
ngx_<module name>_conf_t
结构体,用来表示模块的配置项,这个结构体随模块的不同而不同,通过上下文操作及commands中定义的指令,由配置文件确定其成员值。 -
ngx_<module>_module_t
结构体,表示一组用来初始化ngx_<module name>_conf_t
结构体的操作。在Nginx
初始化时被调用,初始化ngx_<module name>_conf_t
结构体。 -
ngx_command_t
结构体数组,用来表示该模块可以在配置文件中配置的项目,及其操作指令。在Nginx
初始化解析配置文件后,调用ngx_command_t
中的函数set
初始化ngx_<module name>_conf_t
结构体。
总结
在Nginx
中,一个模块运行所需的各种配置参数被封装成了ngx_<module name>_conf_t
结构体。
这个结构体内成员变量的初始化被分成了两部分:
- 模块相关的上下文
ngx_<module>_module_t
:即定义了一组初始化操作,用来将该结构体内的各成员初始化。 - 配置文件相关指令
ngx_command_t
:即规定了配置文件中可以使用的指令及其对应的操作函数。
而模块本身被封装成了ngx_module_t
类型结构体,被用来供Nginx
统一管理和使用。
模块和配置的联系非常紧密,因此,这里仅仅先大略的分析一下模块功能的整体框架,下一篇中,将在本篇的基础上分析Nginx
的配置功能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。