# Nginx 主模块原理 一
我们在前面整体初始化流程中看到,在 Nginx 中,事件循环 ngx_cycle_t 结构和其他与之相关联的结构以及OS信息等等将会在main函数流程中初始化,对于其他功能,采用模块化机制来完成,包括 多路复用器,而笔者也给出了在默认 configure 下 自动生成的 ngx_modules.c 源文件 的生成顺序,本文将详细介绍 核心模块、EPOLL 多路复用器模块的原理。
在开始之前,先通过 Nginx 整体流程 文章中的代码,总结下 模块的功能函数原理以及初始化顺序。
# 模块结构与初始化顺序
从以下与模块相关连的函数中我们得到如下模块函数指针的回调顺序:
核心模块 create_conf -> 核心模块 init_conf -> 模块 init_module -> 模块 init_process -> 模块 exit_process
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle){ // 该代码之前介绍过
if (ngx_cycle_modules(cycle) != NGX_OK) { // 将 ngx_modules 模块信息保存到事件循环
ngx_destroy_pool(pool);
return NULL;
}
// 遍历每个核心模块,并回调他们的 create_conf 函数
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) { // 核心模块不回调 create_conf
continue;
}
module = cycle->modules[i]->ctx; // 核心模块结构指针保存在 模块结构的ctx变量中
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv; // 将创建的配置结构放入 循环结构的 conf_ctx数组,下标为当前模块的索引(注意:该索引下标在main函数 预初始化中已经初始化,之前分析过)
}
}
// 遍历模块文件,回调核心模块结构的 init_conf 函数
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) { // 核心模块不回调 init_conf
continue;
}
module = cycle->modules[i]->ctx; // 核心模块结构指针保存在 模块结构的ctx变量中
if (module->init_conf) {
if (module->init_conf(cycle,
cycle->conf_ctx[cycle->modules[i]->index])
== NGX_CONF_ERROR)
{
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
// 回调模块的 init_module 函数
if (ngx_init_modules(cycle) != NGX_OK) {
/* fatal */
exit(1);
}
}
// 当 ngx_init_cycle 函数初始化完成后,我们将根据配置进入单进程或者多进程模式,这里为了方便分析,以单进程模式来分析,该函数之前也描述过,这里只给出模块相关的函数
void ngx_single_process_cycle(ngx_cycle_t *cycle){
for (i = 0; cycle->modules[i]; i++) { // 调用模块的 init_process 回调函数
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
exit(2);
}
}
}
for (;;){
ngx_process_events_and_timers(cycle); // 本循环的核心在此,所有注意力集中到该方法
if (ngx_terminate || ngx_quit) { // 接收到停止标志位,那么回调模块 exit_process 回调函数,并退出当前循环
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->exit_process) {
cycle->modules[i]->exit_process(cycle);
}
}
}
}
}
// 将前面自动生成的模块信息填入 事件循环
ngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle)
{
cycle->modules = ngx_pcalloc(cycle->pool, (ngx_max_module + 1) * sizeof(ngx_module_t *));
if (cycle->modules == NULL) {
return NGX_ERROR;
}
ngx_memcpy(cycle->modules, ngx_modules,
ngx_modules_n * sizeof(ngx_module_t *));
cycle->modules_n = ngx_modules_n;
return NGX_OK;
}
// 回调 模块的 init_module 函数
ngx_int_t ngx_init_modules(ngx_cycle_t *cycle)
{
ngx_uint_t i;
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->init_module) {
if (cycle->modules[i]->init_module(cycle) != NGX_OK) {
return NGX_ERROR;
}
}
}
return NGX_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
接下来我们来看 模块的结构定义 ngx_module_t 结构。
typedef struct ngx_module_s ngx_module_t; // 模块类型定义
typedef struct ngx_command_s 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; // 通常用于保存后执行函数指针
};
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;
// 模块前七个元数据变量的信息,通常模块使用该宏的初始化前7个变量
#define NGX_MODULE_V1 \
NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX, \
NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE
#define NGX_MODULE_UNSET_INDEX (ngx_uint_t) -1 // -1 表示未指定索引下标
#define nginx_version 1023001 // 当前Nginx版本信息:1.23.1 版本格式为:1(主版本) 023(次版本)001(修订版本)
// 模块签名(感兴趣自己看,长得一批看了好像也没啥卵用- -)
#define NGX_MODULE_SIGNATURE \
NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2 \
NGX_MODULE_SIGNATURE_3 NGX_MODULE_SIGNATURE_4 NGX_MODULE_SIGNATURE_5 \
NGX_MODULE_SIGNATURE_6 NGX_MODULE_SIGNATURE_7 NGX_MODULE_SIGNATURE_8 \
NGX_MODULE_SIGNATURE_9 NGX_MODULE_SIGNATURE_10 NGX_MODULE_SIGNATURE_11 \
NGX_MODULE_SIGNATURE_12 NGX_MODULE_SIGNATURE_13 NGX_MODULE_SIGNATURE_14 \
NGX_MODULE_SIGNATURE_15 NGX_MODULE_SIGNATURE_16 NGX_MODULE_SIGNATURE_17 \
NGX_MODULE_SIGNATURE_18 NGX_MODULE_SIGNATURE_19 NGX_MODULE_SIGNATURE_20 \
NGX_MODULE_SIGNATURE_21 NGX_MODULE_SIGNATURE_22 NGX_MODULE_SIGNATURE_23 \
NGX_MODULE_SIGNATURE_24 NGX_MODULE_SIGNATURE_25 NGX_MODULE_SIGNATURE_26 \
NGX_MODULE_SIGNATURE_27 NGX_MODULE_SIGNATURE_28 NGX_MODULE_SIGNATURE_29 \
NGX_MODULE_SIGNATURE_30 NGX_MODULE_SIGNATURE_31 NGX_MODULE_SIGNATURE_32 \
NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34
struct ngx_module_s {
// 元数据描述信息
ngx_uint_t ctx_index;
ngx_uint_t index;
char *name;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t version;
const char *signature;
void *ctx; // 通常用于保存 ngx_core_module_t 的指针
ngx_command_t *commands; // 该模块所支持的命令(当nginx.conf 解析时找到属性,然后在此找对应命令执行),该指针指向一个命令数组
ngx_uint_t type; // 模块类型
ngx_int_t (*init_master)(ngx_log_t *log); // 在Linux的整个流程中,未回调该函数
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); // 在Linux的整个流程中,未回调该函数
void (*exit_thread)(ngx_cycle_t *cycle); // 在Linux的整个流程中,未回调该函数
void (*exit_process)(ngx_cycle_t *cycle); // 进程退出时调用
void (*exit_master)(ngx_cycle_t *cycle); // 在Linux的流程中,在master - slave 模式中,当master 退出时,将在 ngx_master_process_exit 函数中调用
// 跟Mysql的扩展列一样,然并卵的保留以后使用的函数指针位
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;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# ngx_core_module 模块
由于该模块为 configure 生成模块的首置位,所以我们先看该模块的定义信息。我们可以看到该模块只有 create_conf 和 init_conf 回调。我们观察这两个即可。从源码中,我们可以看到,核心为 ngx_core_conf_t 配置结构,在 create_conf 中创建该结构,同时我们之前看到过 ngx_core_module_create_conf 方法返回后的 ccf 将绑定在 循环结构 对应下标 index处,在 init_conf 中我们将对每个成员变量赋默认值,而对于 ngx_core_commands 而言,将保存一个数组,当解析 nginx.conf (opens new window) 时遇到 其中的关键词,那么用其与之匹配,随后调用其方法覆盖掉 init_conf 中设置的默认值。
ngx_module_t ngx_core_module = {
NGX_MODULE_V1,
&ngx_core_module_ctx, /* module context */
ngx_core_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING // 缓存行填充
};
static ngx_core_module_t ngx_core_module_ctx = {
ngx_string("core"), // 核心模块名
ngx_core_module_create_conf, // 创建配置结构
ngx_core_module_init_conf // 初始化配置结构
};
static ngx_command_t ngx_core_commands[] = { // 用于处理nginx.conf 中 直接写在文件中的配置,没有包含在指定模块配置,核心模块配置太多,这里只给出 默认 配置文件中的 该配置,该配置相比各位道友也不陌生,默认为 1 表示worker 进程数
{ ngx_string("worker_processes"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_set_worker_processes, // 当解析到该词条时,将会回调该方法
0,
0,
NULL },
};
// 创建核心模块的 conf 配置结构,没啥特别的,分配一个空的 配置结构,然后将其成员变量初始化为默认值:#define NGX_CONF_UNSET -1
static void * ngx_core_module_create_conf(ngx_cycle_t *cycle)
{
ngx_core_conf_t *ccf;
ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));
if (ccf == NULL) {
return NULL;
}
ccf->daemon = NGX_CONF_UNSET;
ccf->master = NGX_CONF_UNSET;
ccf->timer_resolution = NGX_CONF_UNSET_MSEC;
ccf->shutdown_timeout = NGX_CONF_UNSET_MSEC;
ccf->worker_processes = NGX_CONF_UNSET;
ccf->debug_points = NGX_CONF_UNSET;
ccf->rlimit_nofile = NGX_CONF_UNSET;
ccf->rlimit_core = NGX_CONF_UNSET;
ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;
ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;
if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) // 初始化配置结构的 env 数组。表示 分配 1 个 ngx_str_t 结构大小
!= NGX_OK)
{
return NULL;
}
return ccf;
}
// 用于初始化配置项,设置默认值
static char * ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_core_conf_t *ccf = conf;
// 初始化默认值
ngx_conf_init_value(ccf->daemon, 1);
ngx_conf_init_value(ccf->master, 1);
ngx_conf_init_msec_value(ccf->timer_resolution, 0);
ngx_conf_init_msec_value(ccf->shutdown_timeout, 0);
ngx_conf_init_value(ccf->worker_processes, 1); // 默认子进程为1,如果我们不在配置里写,也是这个值
ngx_conf_init_value(ccf->debug_points, 0);
#if (NGX_HAVE_CPU_AFFINITY) // OS 能够支持 指定 worker 进程绑定 CPU,那么检测该配置是否合理
if (!ccf->cpu_affinity_auto
&& ccf->cpu_affinity_n
&& ccf->cpu_affinity_n != 1
&& ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes)
{
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"the number of \"worker_processes\" is not equal to "
"the number of \"worker_cpu_affinity\" masks, "
"using last mask for remaining worker processes");
}
#endif
...
#if !(NGX_WIN32)
if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) { // 配置工作目录、用户信息
...
}
... // 配置 lock.file 信息(如果打开了 accept 锁,并且由于编译程序、操作系统架构等因素导致Nginx不支持原子锁,这时才会用文件锁实现accept锁,这时这里设定的 lock_file 的lock 文件生效,后面再分析)
#endif
return NGX_CONF_OK;
}
// 当扫描 nginx 配置文件的 worker_processes 后,将会回调该方法
static char * ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value;
ngx_core_conf_t *ccf;
ccf = (ngx_core_conf_t *) conf;
if (ccf->worker_processes != NGX_CONF_UNSET) { // 已经设置
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "auto") == 0) { // 若设置为自动配置,那么设置为 CPU 核心数
ccf->worker_processes = ngx_ncpu;
return NGX_CONF_OK;
}
ccf->worker_processes = ngx_atoi(value[1].data, value[1].len); // 否则将值取出赋值
if (ccf->worker_processes == NGX_ERROR) {
return "invalid value";
}
return NGX_CONF_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# ngx_events_module 模块
我们从源码中看到该模块,相当于 所有属于 NGX_EVENT_MODULE 类型的事件模块的配置控制模块,由其回调 这些 NGX_EVENT_MODULE 事件类型的 create_conf 和 init_conf 模块。源码如下。
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_core_module_t ngx_events_module_ctx = {
ngx_string("events"),
NULL,
ngx_event_init_conf // 只存在初始化配置回调
};
static ngx_command_t ngx_events_commands[] = {
{ ngx_string("events"), // 对应于 nginx.conf 中的 events 块
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
// 初始化 ngx_events_module 配置
static char * ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
{
#if (NGX_HAVE_REUSEPORT) // 配置了复用端口
ngx_uint_t i;
ngx_listening_t *ls;
#endif
if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) { // 获取该模块的配置(在解析nginx.conf时设置)
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"no \"events\" section in configuration");
return NGX_CONF_ERROR;
}
if (cycle->connection_n < cycle->listening.nelts + 1) { // 检测配置的 worker_connections 选项个数,应该保证 每个 监听 fd 都拥有至少一个连接,所以这里的连接数至少大于等于 监听的 server fd数量
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"%ui worker_connections are not enough "
"for %ui listening sockets",
cycle->connection_n, cycle->listening.nelts);
return NGX_CONF_ERROR;
}
#if (NGX_HAVE_REUSEPORT)
if (!ngx_test_config) { // 若指定复用端口选项开启,那么每个worker 都需要拥有自己的fd(因为 reuseport 允许多个进程绑定同一个port,在内核负载均衡,详情看 reuseport 原理的文章),此时需要复制原始 server fd ,之后每个 worker 从对应下标处获取自己的 fd 即可
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
if (!ls[i].reuseport || ls[i].worker != 0) {
continue;
}
if (ngx_clone_listening(cycle, &ls[i]) != NGX_OK) { // 开始复制
return NGX_CONF_ERROR;
}
ls = cycle->listening.elts;
}
}
#endif
return NGX_CONF_OK;
}
// 解析 events 模块时回调
static char * ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
void ***ctx;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_event_module_t *m;
if (*(void **) conf) {
return "is duplicate";
}
// 获取配置文件中设置的 所属 NGX_EVENT_MODULE 的模块数量(比如我们后面描述的:ngx_event_core_module、ngx_epoll_module 都属于 该模块类型)
ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);
ctx = ngx_pcalloc(cf->pool, sizeof(void *)); // 分配存放 ctx 地址的空间
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *)); // 分配存放子模块地址的数组空间,该地址将存放在上述 ctx 的空间中
if (*ctx == NULL) {
return NGX_CONF_ERROR;
}
*(void **) conf = ctx; // 获取数组第一项的地址放入conf中
for (i = 0; cf->cycle->modules[i]; i++) { // 回调 NGX_EVENT_MODULE 模块的 create_conf 函数,将创建返回的 conf 结构地址存入 ctx 数组对应下标中
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
if (m->create_conf) {
(*ctx)[cf->cycle->modules[i]->ctx_index] =
m->create_conf(cf->cycle);
if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
pcf = *cf; // 将传入的ngx_conf_t *cf 内容保存到 pcf 栈中,此时可以复用 cf 指针所指的 配置内容
// 设置模块信息并解析配置
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
rv = ngx_conf_parse(cf, NULL); // 解析所属 类型 NGX_EVENT_MODULE 模块和命令类型为 NGX_EVENT_CONF的配置
*cf = pcf; // 将保存的内容还原
if (rv != NGX_CONF_OK) {
return rv;
}
for (i = 0; cf->cycle->modules[i]; i++) { // 遍历所有所属 NGX_EVENT_MODULE 类型的模块,并回调 他们的 init_conf 函数
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
if (m->init_conf) {
rv = m->init_conf(cf->cycle, // 传入主循环结构
(*ctx)[cf->cycle->modules[i]->ctx_index]); // 传入 create_conf 创建的该模块的配置结构
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
return NGX_CONF_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131