本文基于 Nginx1.20.0
1. Nginx 模块
1.1 模块类型
高并发是 Nginx 最大的优势之一,而高并发的原因就是 Nginx 强大的事件模块。本文将重点介绍 Nginx 是如果利用 Linux 系统的 Epoll 来完成高并发的。
Nginx 框架定义了 6 种类型的模块,分别是 core、conf、event、stream、http 和 mail,所有的 Nginx 模块都必须属于这 6 类模块。
ngx_module_t 的 type 字段表示模块的类型,取值必须是以下 6 个宏:
#define NGX_CORE_MODULE 0x45524F43 // "CORE"
#define NGX_CONF_MODULE 0x464E4F43 // "CONF"
#define NGX_EVENT_MODULE 0x544E5645 // "EVNT"
#define NGX_STREAM_MODULE 0x4D525453 // "STRM"
#define NGX_HTTP_MODULE 0x50545448 // "HTTP"
#define NGX_MAIL_MODULE 0x4C49414D // "MAIL"
- Core 模块(NGX_CORE_MODULE):目前 Nginx 有 10 个 Core 模块,如下:
**ngx_core_module**ngx_regex_module**ngx_events_module**ngx_http_modulengx_stream_modulengx_thread_pool_modulengx_errlog_modulengx_openssl_modulengx_google_perftools_modulengx_mail_module
- Event 模块(NGX_EVENT_MODULE)目前 Nginx 有 11 个 Event 模块,如下:
**ngx_event_core_module****ngx_epoll_module**(重点关注)ngx_poll_modulengx_select_modulengx_kqueue_modulengx_eventport_modulengx_devpoll_modulengx_aio_modulengx_rtsig_modulengx_poll_module(win32)ngx_select_module(win32)
其它类型模块与本文内容无关,就不列出来了。
对于每一个模块,都有一个 ngx_module_t 类型的结构体表示,结构体的 type 字段表明了模块的类型。
由于本文主要讲解 Linux 系统中使用 Epoll 来完成 Nginx 的事件驱动,故主要介绍 Core 模块的 **ngx_events_module** 与 Event 模块的 **ngx_event_core_module**、**ngx_epoll_module**。
1.2 ngx_modules.c
Nginx 源码根目录中的 configure 脚本会生成源码文件 objs/ngx_modules.c,在这里所有的 Nginx 静态模块都被放进了一个普通的数组,用于启动时的初始化,例如:
// 定义在 objs/ngx_modules.c,是由 configure 脚本动态生成的
ngx_module_t *ngx_modules[] = {
// 模块指针数组
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_openssl_module,
&ngx_regex_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
&ngx_thread_pool_module,
&ngx_http_module,
&ngx_http_core_module,
&ngx_http_log_module,
&ngx_http_upstream_module,
... // 其他模块
&ngx_stream_upstream_zone_module,
NULL
};
ngx_modules 数组简单地把所有 Nginx 模块线性存储在一起。注意,数组只是保存了模块的指针,模块的定义还是在各自实现文件里,所有模块的声明一定不能使用 static,否则 ngx_modules 会无法找到模块,导致编译错误。
另外一个数组 ngx_module_names 保存了所有模块的名称,它与 ngx_modules 是一一对应的。
// 定义在objs/ngx_modules.c(由configure脚本动态生成)
char *ngx_module_names[] = {
// 模块名称数组(与ngx_modules一一对应)
"ngx_core_module",
"ngx_errlog_module",
"ngx_conf_module",
"ngx_openssl_module",
"ngx_regex_module",
"ngx_events_module",
"ngx_event_core_module",
"ngx_epoll_module",
"ngx_thread_pool_module",
"ngx_http_module",
"ngx_http_core_module",
"ngx_http_log_module",
"ngx_http_upstream_module",
... // 其他模块
"ngx_stream_upstream_zone_module",
NULL
}
动态模块这里不讨论!
1.3 ngx_module_t
struct ngx_module_s {
// 下面的几个成员通常使用宏NGX_MODULE_V1填充
// 每类(http/event)模块各自的index,初始化为-1
// ctx_index的初始化:以http模块为例,
// 在ngx_http_module(它是core模块而不是http模块)里,当ngx_http_block()函数
// 解析配置文件http块时,nginx会调用ngx_count_modules()函数,初始化http模块ctx_index成员
ngx_uint_t ctx_index;
// 在ngx_modules数组里的唯一索引,main()里赋值
// 使用计数器变量ngx_max_module
ngx_uint_t index;
// 1.10,模块的名字,标识字符串,默认是空指针
// 由脚本生成ngx_module_names数组,然后在ngx_preinit_modules里填充
// 动态模块在ngx_load_module里设置名字
char *name;
// 两个保留字段,1.9之前有4个
ngx_uint_t spare0;
ngx_uint_t spare1;
// nginx.h:#define nginx_version 1010000
ngx_uint_t version;
// 模块的二进制兼容性签名,即NGX_MODULE_SIGNATURE
const char *signature;
// 模块不同含义不同,通常是函数指针表,是在配置解析的某个阶段调用的函数
// core模块的ctx
//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;
void *ctx;
// 模块支持的指令,数组形式,最后用空对象表示结束
ngx_command_t *commands;
// 模块的类型标识,相当于RTTI,如CORE/HTTP/STRM/MAIL等
ngx_uint_t type;
// 以下7个函数会在进程的启动或结束阶段被调用
// init_master目前nginx不会调用
ngx_int_t (*init_master)(ngx_log_t *log);
// 在ngx_init_cycle里被调用
// 在master进程里,fork出worker子进程之前
// 做一些基本的初始化工作,数据会被子进程复制
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
// 在ngx_single_process_cycle/ngx_worker_process_init里调用
// 在worker进程进入事件循环之前被调用,初始化每个子进程自己专用的数据
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
// init_thread目前nginx不会调用
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
// exit_thread目前nginx不会调用
void (*exit_thread)(ngx_cycle_t *cycle);
// 在ngx_worker_process_exit调用
void (*exit_process)(ngx_cycle_t *cycle);
// 在ngx_master_process_exit(os/unix/ngx_process_cycle.c)里调用
void (*exit_master)(ngx_cycle_t *cycle);
// 下面8个成员通常用用NGX_MODULE_V1_PADDING填充
// 暂时无任何用处
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. 事件模块初始化
众所周知,Nginx 是 Master/Worker 框架,在 Nginx 启动时是一个进程,在启动的过程中 Master 会 fork(2) 出了多个子进程作为 Worker(数量由 worker_processes 配置决定)。
主进程(Master)单实例运行,负责管理 Worker 进程,不直接处理客户端请求。而工作进程(Worker),多实例运行,实际处理请求(如 HTTP 连接、反向代理等)。
因此,事件模块的初始化也是分成两部分。一部分发生在 fork(2) 之前,主要是 Master 进程解析配置文件等操作,另外一部分发生在 fork(2) 之后,主要是 Worker 进程向 Epoll 中添加监听事件。
2.1 Master 进程对事件模块的初始化
启动进程对事件模块的初始化分为配置文件解析、监听端口和 ngx_event_core_module 模块的初始化。这三个步骤均在 ngx_init_cycle 函数进行。
调用关系:
main()
|-> ngx_init_cycle()
ngx_init_cycle 函数的流程:
- 创建
cycle->conf_ctx数组; - 拷贝全局数组
ngx_modules到cycle->modules; - 调用所有
core模块的create_conf函数指针,创建出配置结构,填入数组; - 设置
ngx_conf_t的各个字段,作为配置解析的起始ctx; - 执行
ngx_conf_parse(),在模块数组里查找配置指令对应的解析函数并处理; - 反复执行步骤5,直至整个配置文件处理完毕;
- 调用
core模块init_conf函数指针,初始化core模块的配置。
红框是本节将要介绍的三部分内容。

2.1.1 Core 模块:ngx_events_module
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, // create_conf
// 1.15.2之前只检查是否创建了配置结构体,无其他操作
// 因为event模块只有一个events指令
// 1.15.2之后增加新的代码
ngx_event_init_conf // init_conf
};
// events模块仅支持一个指令,即events块
static ngx_command_t ngx_events_commands[] = {
{
ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
2.1.2 解析配置文件
ngx_cycle_t
ngx_cycle_t 不仅存储了所有的模块信息、监听端口,也保存了所有的配置解析的其它相关信息。完全可以将其看成配置文件在内存中的表示。
// 定义在 core/ngx_cycle.h
struct ngx_cycle_s {
void ****conf_ctx;
...
ngx_module_t **modules;
...
ngx_array_t listening; // 监听的端口数组
...
}
conf_ctx 是 Nginx 存储配置数据的起点,但它的声明使用了连续四个星号,乍一看很难立即理解其含义。但实际上 conf_ctx 是一个二维数组,数组里的元素是 void* 指针,每一个指针又指向了另一个存储 void* 的数组(实际对应到 event 模块的配置存储)。

配置文件解析由 main() -> ngx_init_cycle() 函数完成,但 ngx_init_cycle() 在实际开始解析配置文件之前,会先给 cycle->conf_ctx 数组分配内存,调用所有 Core 模块的 create_conf 方法(如果实现了的话)。
ngx_conf_t
ngx_conf_t 结构定义在 core/ngx_config_file.h,是 Nginx 解析配置文件时的重要数据结构,表示解析当前配置指令时的运行环境数据(Context):
// 定义在core/ngx_core.h
typedef struct ngx_conf_s ngx_conf_t;
// 定义在core/ngx_config_file.h
struct ngx_conf_s {
ngx_array_t *args; // 存储指令字符串数组(如["server", "127.0.0.1"])
ngx_pool_t *pool; // 内存池对象
ngx_log_t *log; // 日志对象
void *ctx; // 当前环境(如ngx_http_conf_ctx_t)
ngx_uint_t cmd_type; // 当前命令类型
...
ngx_conf_file_t *conf_file; // 当前的配置文件
... // 其他数据成员
};
因为 Nginx 的配置文件是有层次的,分成了 main / http / server / location 等作用域,不同的域里指令的含义和处理方式都有可能不同(例如 http{} 块里的 server 指令和 upstream{} 块里的 server 指令就是完全不同的),所以 Nginx 在解析配置文件时必须使用 ngx_conf_t 来保存当前的基本信息,进入/退出一个配置块都会变更 ngx_conf_t,指令的解析必须要参考 ngx_conf_t 环境数据才能正确处理。
args 参数是我们最常用到的成员,它是一个 ngx_array_t,以 ngx_str_t 形式存储分割好的配置文件指令字符串。如果有 NgxStrArray arr(cf->args),那么 arr[0] 就是指令名,arr[1] 就是第一个参数,以此类推。
为 cycle->conf_ctx 数组分配内存
在 _ngx_cycle.c_ 的 ngx_init_cycle() 函数里创建 conf_ctx 数组的代码是:
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void*));
这实际上就是一个存储 void* 指针的普通数组,长度为 ngx_max_module,可以用来存放所有模块的配置数据结构(但实际上 Nginx 并没有这么做)。
Nginx 通过这种方式灵活地支持了不同模块的数据存储需求:对于 Core 模块,指针直接指向配置数据;而 event / stream / http 模块则指针指向的可以是另外一个数组,形成了一个树形结构。
创建 Core 模块的配置结构
调用 Core 模块的 create_conf 函数指针创建 Core 模块的配置结构。
// 初始化core模块
for (i = 0; cycle->modules[i]; i++) {
// 检查type,只处理core模块,数量很少
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
//获取core模块的函数表
module = cycle->modules[i]->ctx;
// 创建core模块的配置结构体
// 有的core模块可能没有这个函数,所以做一个空指针检查
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
// 存储到cycle的配置数组里,用的是index,不是ctx_index
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
ngx_events_module 模块没有实现 create_conf 函数指针,但是实现了 init_conf,即 ngx_event_init_conf。
因此,ngx_events_module 这个 Core 模块无法调用 create_conf 创建 ngx_events_module 模块的配置结构,并将其指针填充到 cycle->conf_ctx 数组中。只有等到解析配置文件时,遇到 events 块指令,才由 events 块指令处理函数 ngx_events_block() 来创建该配置结构。懒创建,节省了不必要的内存浪费。
调用完 Core 模块的 create_conf 后,cycle->conf_ctx 数组如下图所示:

配置文件解析入口
ngx_init_cycle() 在准备好配置解析的上下文信息后,调用 ngx_conf_parse() 开始读取解析主配置文件。
ngx_conf_parse() 主干代码如下:
// 解析配置,参数filename可以是NULL
char* ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
....
// 有文件名,解析文件
if (filename) {
/* open configuration file */
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
...
// 保存当前的配置文件
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;
// 准备新的解析上下文信息ngx_conf_t
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;
cf->conf_file->line = 1;
// 标记当前是解析文件
type = parse_file;
...
} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
// 标记当前是解析配置块
type = parse_block;
} else {
// 解析-g传递的命令行参数
type = parse_param;
}
for ( ;; ) {
// 从配置文件里读取token,保存在cf->args数组里
rc = ngx_conf_read_token(cf);
/*
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/
...
// 在所有模块里查找匹配的指令,进行解析
rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) {
goto failed;
}
}
failed:
rc = NGX_ERROR;
done:
// 配置文件解析完毕
if (filename) {
if (cf->conf_file->buffer->start) {
ngx_free(cf->conf_file->buffer->start);
}
// 关闭配置文件
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " %s failed",
filename->data);
rc = NGX_ERROR;
}
// 恢复之前解析的配置文件,例如include指令,继续向下解析
cf->conf_file = prev;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
在 Nginx 中,使用 ngx_conf_file_t 表示一个配置文件。
include 配置指令用来在当前配置文件中引入一个新的配置文件。在解析到 include "conf_file" 指令时,会暂停当前配置文件的解析,进入新配置文件 conf_file 中解析。
通过递归调用 ngx_conf_parse(),最终完成所有配置文件的解析。
ngx_conf_parse() 根据其第 2 参数 filename 文件名是否为空,进行不同的操作:
filename不为 null,这时需要打开新的配置文件,for(;;)循环读取新配置文件中的配置指令,并交给ngx_conf_handler处理。filename为 null,检查当前配置文件是不是个有效的配置文件(cf->conf_file->file.fd)- 有效(
fd != -1):说明解析到一个块{},接下就是for(;;)循环读取块中指令,然后交给ngx_conf_handler()处理; - 无效(
fd == -1):最后一种情况,是-g命令行参数传递的指令。
- 有效(
-g命令行参数传递的指令也会被包装成一个配置文件ngx_conf_file_t,然后交给ngx_conf_parse(cf, NULL)处理。
Q:什么时候会调用
ngx_conf_parse?
- 程序刚启动时,会将主配置文件封装成
ngx_conf_file_t,然后调用ngx_conf_parse(cf, "main_conf")。- 程序启动时,如果使用
-g命令行参数传递了指令,会在ngx_init_cycle() -> ngx_conf_param()中,将指令包装成配置文件ngx_conf_file_t(其中 fd 置为 -1)并保存到cf->conf_file中,然后调用ngx_conf_parse(cf, NULL)处理。- 如果解析到
include "new_conf"指令(由ngx_conf_handler()在所有模块中检索),include指令的处理函数ngx_conf_include()会调用ngx_conf_parse(cf, "new_conf")。- 如果解析到块指令(如,events、http 等),会调用块指令的处理函数,块指令处理函数会调用
ngx_conf_parse(cf, NULL)。由于当前配置文件必然是个有效的配置文件,所以会进入块中,继续解析块中的指令。
接下来我们只关注配置文件中对 events{...} 块的解析。
解析 events{...} 块
我们以 events{...} 块中的 use epoll 为例讲解。
调用链:
main()
|-> ngx_init_cycle()
|-> ngx_conf_parse() // filename是主配置文件,for(;;)循环读取配置指令
|-> ngx_conf_handler() // 将读取的指令字符串(events)与所有模块的预定义指令匹配
|-> ngx_events_block() // 执行events指令的处理函数
|-> ngx_conf_parse() // filename为null,for(;;)循环读取events块中的配置指令
|-> ngx_conf_handler // 将读取的指令字符串(use)与所有模块的预定义指令(use)匹配
|-> ngx_event_use // 执行use指令的处理函数
ngx_events_block()
ngx_events_block() 是块命令 events 的处理函数。
events 命令定义在 ngx_events_module(Core 模块),也是该模块唯一的指令,代码如下。
static ngx_command_t ngx_events_commands[] = {
{
ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
分配事件模块的配置数组
当 ngx_conf_parse() 解析到 events 命令后,会调用 ngx_conf_handler() -> ngx_events_block() 处理。文章开头列出了 Event 类型的模块多达 9 个。ngx_events_block() 在递归调用 ngx_conf_parse() 块中的指令(如 use epoll)之前,需要计算出所有 Event 模块的数量,并为他们分配一个指针数组。每个数组元素都是一个指向具体 Event 模块配置结构的指针。然后,调用各个事件模块的 create_conf,创建自己的配置结构,并将指针填充到配置数组中。
// 统计所有的事件模块数量,设置事件模块的ctx_index
ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);
// ctx是void***,也就是void** *,即指向二维数组的指针
ctx = ngx_pcalloc(cf->pool, sizeof(void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
// 分配存储事件模块配置的数组
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
}
// 在cycle->conf_ctx里存储这个指针
*(void **) conf = ctx;
// 对每一个事件模块调用create_conf创建配置结构体
// 事件模块的层次很简单,没有多级,所以二维数组就够了
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
// 调用create_conf创建配置结构体
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;
}
}
}
我们依然只关注 ngx_event_core_module 和 ngx_epoll_module 这两个事件模块。
ngx_event_core_module模块的create_conf函数指针实现是ngx_event_core_create_conf。
static ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name, // event_core模块的名字:"event_core"
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
...
};
继续跟进 ngx_event_core_create_conf 做了什么?
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle)
{
ngx_event_conf_t *ecf;
ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
if (ecf == NULL) {
return NULL;
}
ecf->connections = NGX_CONF_UNSET_UINT;
ecf->use = NGX_CONF_UNSET_UINT;
ecf->multi_accept = NGX_CONF_UNSET;
ecf->accept_mutex = NGX_CONF_UNSET;
ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
ecf->name = (void *) NGX_CONF_UNSET;
...
return ecf;
}
创建了一个属于 ngx_event_core_module 模块的配置结构 ngx_event_conf_t,并对该结构进行强制初始化(-1)。
ngx_event_core_module 模块中定义了许多指令,如 use、worker_connections、accept_mutex 等等。
ngx_event_conf_t 结构中的字段和其指令是一一对应的(名称类似)。你在 events{} 块中配置的指令参数,都会保存到指令所在事件模块的配置结构的相应字段中。如,worker_connections 1024; 会将 1024 保存到 ngx_event_conf_t 结构的 connections 字段中
所有事件模块的指令都需要放在
events{}块中。
ngx_epoll_module模块的create_conf函数指针实现是ngx_epoll_create_conf。
static ngx_event_module_t ngx_epoll_module_ctx = {
// epoll模块的名字"epoll"
&epoll_name,
// 创建配置结构体
ngx_epoll_create_conf, /* create configuration */
// 初始化配置结构体
ngx_epoll_init_conf, /* init configuration */
...
};
同样的,创建了一个属于 ngx_epoll_module 模块的配置结构 ngx_epoll_conf_t,并对该结构进行了默认初始化(-1)。
static void *
ngx_epoll_create_conf(ngx_cycle_t *cycle)
{
ngx_epoll_conf_t *epcf;
epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));
if (epcf == NULL) {
return NULL;
}
// 两个值都是数字,所以要置为-1
epcf->events = NGX_CONF_UNSET;
epcf->aio_requests = NGX_CONF_UNSET;
return epcf;
}
根据 ngx_epoll_conf_t 可知,ngx_epoll_module 模块只定义了两个指令 epoll_events,worker_aio_requests。
解析 events 块中的指令
同样地,递归调用 ngx_conf_parse() 读取解析 events 块中的指令,这里就不重复说了。
// 暂存当前的解析上下文
pcf = *cf;
// 设置事件模块的新解析上下文
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
// 递归解析事件相关模块
rv = ngx_conf_parse(cf, NULL);
在 ngx_conf_parse() 中,for(;;)循环读取到指令 use epoll; 后,交给 ngx_conf_handler() -> ngx_event_use() 处理。
static ngx_command_t ngx_event_core_commands[] = {
...
{
ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
...
}
events 块解析完毕
// 递归解析事件相关模块
rv = ngx_conf_parse(cf, NULL);
// 恢复之前保存的解析上下文
*cf = pcf;
if (rv != NGX_CONF_OK) {
return rv;
}
ngx_conf_parse() 返回后,说明对 events{...} 块的解析已经完成,这时,各个事件模块的配置结构也已经被配置文件中其所属的指令参数填充完毕。没有指定的,会在后面对其填充默认值。如,配置文件中没有 accept_mutex_delay,后面会被赋予 512ms 的默认值。
这时,cycle->conf_ctx 数组如下图所示:

填充配置默认值
对于没有在 events 块中出现的指令,在各个事件模块的 init_conf() 中都会给与其默认值。
// 解析完毕,需要初始化默认配置值
for (i = 0; cf->cycle->modules[i]; i++) {
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]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
对于,ngx_event_core_module 和 ngx_epoll_module 这两个事件模块,其 init_conf 函数指针分别对应 ngx_event_core_init_conf 和 ngx_epoll_init_conf。
ngx_event_core_init_conf定义在 _event/ngx_event.c_
static char* ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_event_conf_t *ecf = conf;
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
int fd;
#endif
ngx_int_t i;
ngx_module_t *module;
ngx_event_module_t *event_module;
module = NULL;
// 测试epoll是否可用
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
fd = epoll_create(100);
// epoll调用可用,那么模块默认使用epoll
if (fd != -1) {
(void) close(fd);
// epoll调用可用,那么模块默认使用epoll
module = &ngx_epoll_module;
} else if (ngx_errno != NGX_ENOSYS) {
// epoll调用可用,那么模块默认使用epoll
module = &ngx_epoll_module;
}
#endif
#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)
module = &ngx_devpoll_module;
#endif
#if (NGX_HAVE_KQUEUE)
module = &ngx_kqueue_module;
#endif
// 如果epoll不可用,那么默认使用select
#if (NGX_HAVE_SELECT)
if (module == NULL) {
module = &ngx_select_module;
}
#endif
// 还没有决定默认的事件模型
if (module == NULL) {
// 遍历所有的事件模块
for (i = 0; cycle->

最低0.47元/天 解锁文章
249

被折叠的 条评论
为什么被折叠?



