# Nginx EPOLL 事件模块 action 回调原理
前面我们详细描述了各个模块之间的交互流程,本节我们将详细分析 EPOLL 事件模块如何处理事件。我们先来看如下结构,很简单,我们也看到过:ngx_event_actions_t 结构中将保存用于完成对应事件的函数指针,而 EPOLL 模块正是实现了这些函数指针来完成相应动作,我们只需要看看 EPOLL 如何 完成这些 函数指针的实现即可。
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*notify)(ngx_event_handler_pt handler);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
// 进程 全局变量 表示当前多路复用器的回调函数变量
ngx_event_actions_t ngx_event_actions;
// 用于操作多路复用器的宏定义,可以很明显看到:直接回调其中注册的函数,这里便是 epoll 模块的实现
#define ngx_process_events ngx_event_actions.process_events
#define ngx_done_events ngx_event_actions.done
#define ngx_add_event ngx_event_actions.add
#define ngx_del_event ngx_event_actions.del
#define ngx_add_conn ngx_event_actions.add_conn
#define ngx_del_conn ngx_event_actions.del_conn
#define ngx_notify ngx_event_actions.notify
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
我们看到过,EPOLL 模块实现了如下 回调函数:
static ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
#if (NGX_HAVE_EVENTFD)
ngx_epoll_notify, /* trigger a notify */
#else
NULL, /* trigger a notify */
#endif
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ngx_epoll_init 函数
该函数我们看到将在 event core 模块中的 ngx_event_process_init 方法中回调,而 event core 模块 的ngx_event_process_init 方法,将会在 event core 模块 在 worker进程 启动时回调。
// event core 模块 回调 epoll 模块的 ngx_epoll_init 函数
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle){
...
for (m = 0; cycle->modules[m]; m++) {
if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
if (cycle->modules[m]->ctx_index != ecf->use) {
continue;
}
module = cycle->modules[m]->ctx;
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
exit(2);
}
break;
}
...
}
// worker 进程创建成功后调用 event core 模块 的 ngx_event_process_init 方法
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker){
...
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
exit(2);
}
}
}
...
}
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
接下来我们直接来看 ngx_epoll_init 方法的实现。我们看到该方法前面也描述过,为每个进程 创建自己的 epoll fd,每个worker 进程使用该 epoll fd 来管理自己的事件。
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer){
ngx_epoll_conf_t *epcf;
epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
if (ep == -1) {
ep = epoll_create(cycle->connection_n / 2);
if (ep == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"epoll_create() failed");
return NGX_ERROR;
}
#if (NGX_HAVE_EVENTFD) // 初始化 event fd
if (ngx_epoll_notify_init(cycle->log) != NGX_OK) {
ngx_epoll_module_ctx.actions.notify = NULL;
}
#endif
#if (NGX_HAVE_FILE_AIO) // 初始化 AIO 相关
ngx_epoll_aio_init(cycle, epcf);
#endif
#if (NGX_HAVE_EPOLLRDHUP) // 测试epoll 是否支持 2.6.7 以上 的 EPOLLRDHUP 事件(用于检测 连接的对端 断开连接)
ngx_epoll_test_rdhup(cycle);
#endif
}
if (nevents < epcf->events) { // 初始化事件列表
if (event_list) {
ngx_free(event_list);
}
event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
nevents = epcf->events; // 总事件数
ngx_io = ngx_os_io;
ngx_event_actions = ngx_epoll_module_ctx.actions;
#if (NGX_HAVE_CLEAR_EVENT) // 配置使用 EPOLL 的 LT 或者 ET 模式,默认 LT 水平触发模式
ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
|NGX_USE_GREEDY_EVENT
|NGX_USE_EPOLL_EVENT;
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
# ngx_epoll_add_event 函数
该函数将用于向 EPOLL 中注册事件。接下来我们来看该函数的实现。源码相对简单:根据添加事件的类型,调用 (epoll_ctl(ep, op, c->fd, &ee) 系统调用,将事件添加到epoll的监听列表中,我们看到这里使用 struct epoll_event 的 data 数据域 来保存与之关联的 ngx_connection_t 结构。源码如下。
static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee;
c = ev->data;
events = (uint32_t) event;
if (event == NGX_READ_EVENT) { // 注册读事件(事件类型:EPOLLIN)
e = c->write;
prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
events = EPOLLIN|EPOLLRDHUP;
#endif
} else { // 注册写事件(事件类型:EPOLLOUT)
e = c->read;
prev = EPOLLIN|EPOLLRDHUP;
#if (NGX_WRITE_EVENT != EPOLLOUT)
events = EPOLLOUT;
#endif
}
if (e->active) { // 之前已经注册过,此时 e active 有效位置位,那么表示使用 EPOLL_CTL_MOD 标志位,表示修改 epoll 监听的事件类型
op = EPOLL_CTL_MOD;
events |= prev;
} else { // 否则为添加操作
op = EPOLL_CTL_ADD;
}
#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP) // 配置 且 OS 支持 多进程监听 同一个 socket 并由OS 自身维护互斥,那么检测 NGX_EXCLUSIVE_EVENT 标志位,若存在,那么不需要监听 对端关闭的事件 其中 ( #define NGX_EXCLUSIVE_EVENT EPOLLEXCLUSIVE)
if (flags & NGX_EXCLUSIVE_EVENT) {
events &= ~EPOLLRDHUP;
}
#endif
ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); // 将 ngx_connection_t 与 instance 位关联(其中 : unsigned instance:1 占用一个字节,也即低位一字节,用于表示 事件是否已经过期)
if (epoll_ctl(ep, op, c->fd, &ee) == -1) { // 标准的 epoll CRUD 接口
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
ev->active = 1; // 添加操作,添加成功后,此时 ngx_event_t 有效
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
# ngx_epoll_del_event 函数
该函数用于从 epoll 监听列表中删除指定的事件。我们看到该函数若在 ngx_event_t 有效时(active变量为1)那么等价于修改监听标志位 操作(通常我们可以将标志位设置为 epoll 不存在的监听类型,此时等效于 屏蔽指定 fd 的 事件通知),否则 等价于 删除操作。源码如下。
static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee;
if (flags & NGX_CLOSE_EVENT) { // 表示此时文件描述符 fd 已经关闭,此时 epoll 会自动将其从监听列表中移除,我们不需要操作 epoll 接口
ev->active = 0;
return NGX_OK;
}
c = ev->data; // 获取与之绑定的 ngx_connection_t 结构
if (event == NGX_READ_EVENT) { // 注册的事件类型为读事件
e = c->write;
prev = EPOLLOUT;
} else { // 注册的事件类型为写事件
e = c->read;
prev = EPOLLIN|EPOLLRDHUP; // 用于打印日志,这里无视就好,我已经删除了 日志打印 代码
}
if (e->active) { // 若此时 ngx_event_t 仍然有效,那么该函数将等效于 修改 监听标志位
op = EPOLL_CTL_MOD;
ee.events = prev | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
} else { // 否则 执行 删除操作,将 fd 从 epoll 中删除
op = EPOLL_CTL_DEL;
ee.events = 0;
ee.data.ptr = NULL;
}
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
ev->active = 0; // 该函数执行完毕后,将 事件 设置为 失效状态
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
# ngx_epoll_add_connection 函数
该函数直接用于向 epoll 中添加新的连接,所以比较直接,不判断是否存在,直接指定 EPOLL_CTL_ADD 添加新连接。注意此时将会监听 新注册 的 fd 的 所有事件:读事件(EPOLLIN)、写事件(EPOLLOUT)、触发方式(EPOLLET指定边缘触发)、对端关闭连接事件(EPOLLRDHUP)。此时很明显看到若调用该函数,默认触发方式为 边缘触发!。
static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c)
{
struct epoll_event ee;
ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP; // 边缘触发
ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);
if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd);
return NGX_ERROR;
}
// 读写事件 都生效
c->read->active = 1;
c->write->active = 1;
return NGX_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ngx_epoll_del_connection 函数
该函数用于将 监听的 fd 直接从 epoll 监听列表中删除,此时将不再判断 ngx_event_t 是否有效,因为此时 于 ngx_event_t 结构无关。源码如下。
pass:其实我们不难发现,操作 ngx_connection_t 为上层结构,而 ngx_event_t 嵌入在 ngx_connection_t 中,上面操作 ngx_event_t 的函数,将会根据 ngx_event_t 事件来完成具体动作,而这里的 ngx_epoll_add_connection 函数 和 ngx_epoll_del_connection 函数的 ngx_connection_t 操作将无视所有,直接关闭或者添加~
static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)
{
int op;
struct epoll_event ee;
if (flags & NGX_CLOSE_EVENT) {
c->read->active = 0;
c->write->active = 0;
return NGX_OK;
}
op = EPOLL_CTL_DEL;
ee.events = 0;
ee.data.ptr = NULL;
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
// 删除后读写事件均无效
c->read->active = 0;
c->write->active = 0;
return NGX_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ngx_epoll_notify 函数
该函数用于唤醒调用 epoll_wait 等待事件的 EPOLL 进程,我们可以看到这里直接使用之前创建的 notify_fd 来通知,此时由于写入了数据,当再次调用 epoll_wait 时,由于存在该 notify_fd 存在 写事件,将直接返回,不会阻塞。
#if (NGX_HAVE_EVENTFD) // 只有存在 EPOLL FD 才能使用该函数
static ngx_int_t ngx_epoll_notify(ngx_event_handler_pt handler)
{
static uint64_t inc = 1;
notify_event.data = handler;
if ((size_t) write(notify_fd, &inc, sizeof(uint64_t)) != sizeof(uint64_t)) {
ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,
"write() to eventfd %d failed", notify_fd);
return NGX_ERROR;
}
return NGX_OK;
}
#endif
2
3
4
5
6
7
8
9
10
11
12
13
# ngx_epoll_process_events 函数
该函数用于处理 epoll 中注册的事件。我们知道由于定时器的存在,所以不可能让 epoll_wait 一直等待下去,所以具体的等待事件的时间需要根据最少等待时间的定时器 ngx_msec_t timer 来决定,所以在 epoll_wait(ep, event_list, (int) nevents, timer)的时间将为 timer,那么此时就会存在两种情况:
1、超时了还没有事件:检测无异常退出
2、未超时存在事件:循环处理事件即可(若指定延迟处理,那么挂入队列。若未指定,那么立即调用 handler )
// 三大变量分别用于表示:epoll fd、接收准备好事件的数组、事件数
static int ep = -1;
static struct epoll_event *event_list;
static ngx_uint_t nevents;
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int events;
uint32_t revents;
ngx_int_t instance, i;
ngx_uint_t level;
ngx_err_t err;
ngx_event_t *rev, *wev;
ngx_queue_t *queue;
ngx_connection_t *c;
events = epoll_wait(ep, event_list, (int) nevents, timer); // 等待事件发生或者超时,全局变量
err = (events == -1) ? ngx_errno : 0; // 检测错误
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { // 若指定等待后更新缓存的时间结构,那么进行更新(时间模块我们后面再单独用一篇文章来描述)
ngx_time_update();
}
if (err) { // 若发生异常,那么打印日志
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
return NGX_ERROR;
}
if (events == 0) { // 第一种情况:定时器超时,且未发生任何事件,那么直接返回(若指定定时器不存在,但居然没有任何事件产生 epoll 竟然返回了,这种异常情况必定打印 错误日志)
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"epoll_wait() returned no events without timeout");
return NGX_ERROR;
}
// 第二种情况,定时器未超时且存在事件,那么遍历处理即可
for (i = 0; i < events; i++) {
c = event_list[i].data.ptr; // 获取之前保存的 ngx_connection_t 结构
instance = (uintptr_t) c & 1; // 检测是否有效
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); // 将指针最后一位保存是否有效位移出,还原真正的 ngx_connection_t 指针
rev = c->read; // 获取读事件
if (c->fd == -1 || rev->instance != instance) { // 此时表示 nginx 已经修改了当前连接,那么此时准备好的事件的 fd 为过期的fd,那么打印日志,直接返回
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
revents = event_list[i].events; // 获取当前 监听的fd 准备好的事件标志位
if (revents & (EPOLLERR|EPOLLHUP)) { // 若当前 监听的fd 发生了错误事件,那么在事件类型上添加 EPOLLIN 和 EPOLLOUT,让后面的事件处理器 来处理这种错误状态,否则,将没有回调的事件处理器来处理该状态(我就想问下:为毛不注册一个 单独处理 这种错误事件的处理器呢??读者也可以思考下?这是完全可以的)
revents |= EPOLLIN|EPOLLOUT;
}
if ((revents & EPOLLIN) && rev->active) { // 处理读事件
#if (NGX_HAVE_EPOLLRDHUP)
if (revents & EPOLLRDHUP) { // 对端关闭事件设置 pending_eof 标志位
rev->pending_eof = 1;
}
#endif
rev->ready = 1; // 标志读事件已经准备好
rev->available = -1;
if (flags & NGX_POST_EVENTS) { // 若当前指定需要暂时将事件挂到 事件列表中,那么这里将不会回调 注册的事件时指定的 handler 函数,仅仅添加到对应列表即可(接受连接事件,那么 添加到 ngx_posted_accept_events队列,其他事件 : ngx_posted_events队列)
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue); // 普通的 add 添加队列操作
} else { // 回调注册的函数
rev->handler(rev);
}
}
// 处理写事件
wev = c->write;
if ((revents & EPOLLOUT) && wev->active) { // 同读事件一样的处理方式
if (c->fd == -1 || wev->instance != instance) {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
wev->ready = 1; // 标志写事件已经准备好
if (flags & NGX_POST_EVENTS) { // 同读事件一样
ngx_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
}
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
93
那么,此时来看以下代码是否熟悉:
void ngx_single_process_cycle(ngx_cycle_t *cycle){
...
for ( ;; ) { // 循环处理所有事件,知道 nginx 停止
ngx_process_events_and_timers(cycle); // 本循环的核心在此,所有注意力集中到该方法
}
...
}
void ngx_process_events_and_timers(ngx_cycle_t *cycle) {
...
if (ngx_timer_resolution) { // 不执行时间事件,那么设置timer 为 -1( #define NGX_TIMER_INFINITE (ngx_msec_t) -1 )
timer = NGX_TIMER_INFINITE;
flags = 0;
} else { // 否则从保存时间事件的红黑树中找到 最近需要执行的定时器 timer
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME; // 设置标志位为需要更新时间
}
if (ngx_use_accept_mutex) { // 当设置需要使用 接收互斥锁时,尝试获取锁(防止多进程条件下 epoll 监听server fd 导致惊群现象发生)
if (ngx_accept_disabled > 0) { // 用于多进程处理连接事件的负载均衡(当前进程的该值大于0,那么不处理连接事件,后面再说)
ngx_accept_disabled--;
} else { // 否则尝试获取互斥锁
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { // 获取锁发生异常
return;
}
// 获取锁未发生异常,那么可能:获得锁或者未获得锁
if (ngx_accept_mutex_held) { // 进程持有互斥锁那么可以执行接收事件(可以看到,这里指定 flags 为 NGX_POST_EVENTS的意义: 延迟处理所有事件,意义何在?快速返回,不会长时间持有锁操作 ,好好体会)
flags |= NGX_POST_EVENTS;
} else { // 未持有互斥锁,那么当不执行时间时间时或者待执行的时间事件的执行时间大于 ngx_accept_mutex_delay 延迟时间,那么将定时器设置为 ngx_accept_mutex_delay(默认 500 ms)
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
(void) ngx_process_events(cycle, timer, flags); // 调用 #define ngx_process_events 此时很明显调用了epoll 的 ngx_epoll_process_events
...
ngx_event_process_posted(cycle, &ngx_posted_accept_events); // 立即处理 accept 事件队列
if (ngx_accept_mutex_held) { // 然后【作者:黄俊,微信:bx_java】立即释放锁(为何不先释放锁再执行 accept 队列?一看 epoll 就理解不深入,若释放了 其他进程同样也能检测到同样的事件为何?因为当前进程还没有处理该事件)
ngx_shmtx_unlock(&ngx_accept_mutex);
}
// 释放锁后再处理 定时器和其他读写事件,此时保证了快速高效的减少锁范围,因为 对于当前进程的 epoll 来说,自身的读写事件 与 其他进程无关,所以处理完 accept 后 应该释放锁再干别的!
ngx_event_expire_timers();
ngx_event_process_posted(cycle, &ngx_posted_events);
}
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
# ngx_epoll_done 函数
该函数为 epoll 模块的清理操作,可以看到:将所有创建的 fd 和 事件列表全部删除。
static void ngx_epoll_done(ngx_cycle_t *cycle){
if (close(ep) == -1) { // 关闭 epoll
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"epoll close() failed");
}
ep = -1;
#if (NGX_HAVE_EVENTFD) // 关闭用于通知的 event fd
if (close(notify_fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"eventfd close() failed");
}
notify_fd = -1;
#endif
#if (NGX_HAVE_FILE_AIO) // 关闭 aio
if (ngx_eventfd != -1) {
if (io_destroy(ngx_aio_ctx) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"io_destroy() failed");
}
if (close(ngx_eventfd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"eventfd close() failed");
}
ngx_eventfd = -1;
}
ngx_aio_ctx = 0;
#endif
ngx_free(event_list); // 删除事件列表
event_list = NULL;
nevents = 0;
}
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