概述
Nginx源码中,实现IO多路复用的机制的的文件主要在ngx_epoll_module.c中,本文也主要分析epoll模块的实现。主要从创建、初始化和管理epoll实例入手分析。
实现逻辑分析
- 初始化:ngx_epoll_init 负责创建epoll实例,同时对实例进行初始化
- 事件注册:ngx_epoll_add_event,主要负责将要关心的事件以及文件描述符添加到epoll实例中
- 事件等待与处理:事件循环过程中,调用ngx_epoll_process_event 等待事件发生,同事并处理这些事件
- 事件删除:ngx_epoll_del_event 负责从epoll实例中删除不需要监控的事件内容
- 清理:通过ngx_epoll_done关闭epoll实例,同时释放对应内存资源
ngx_epoll_init
初始化epoll实例,创建epoll的文件描述符
- 使用epoll_create创建爱一个epoll实例,设置其大小,同时检测其是否创建成功
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) {
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;
}
// 其他初始化代码
return NGX_OK;
}
ngx_epoll_done
关闭epoll实例,同时释放对应资源
static void ngx_epoll_done(ngx_cycle_t *cycle) {
if (close(ep) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll close() failed");
}
// 其他清理代码
}
ngx_epoll_add_event
将事件添加到epoll事例中,然后检查是否添加成功
static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
struct epoll_event ee;
ee.events = event;
ee.data.ptr = ev;
if (epoll_ctl(ep, EPOLL_CTL_ADD, ev->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_ctl(EPOLL_CTL_ADD) failed");
return NGX_ERROR;
}
return NGX_OK;
}
ngx_epoll_del_event
从epoll实例中删除关心的事件
static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
struct epoll_event ee;
ee.events = 0;
ee.data.ptr = NULL;
if (epoll_ctl(ep, EPOLL_CTL_DEL, ev->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_ctl(EPOLL_CTL_DEL) failed");
return NGX_ERROR;
}
return NGX_OK;
}
nagx_epoll_process_events
等待并处理epoll事件,将关心事件中已经发生的事件存储到event_list中,同时遍历每个事件
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
int events;
struct epoll_event event_list[NGX_MAX_EVENTS];
events = epoll_wait(ep, event_list, NGX_MAX_EVENTS, timer);
if (events == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_wait() failed");
return NGX_ERROR;
}
for (int i = 0; i < events; i++) {
ngx_event_t *ev = event_list[i].data.ptr;
// 处理事件
}
return NGX_OK;
}
处理事件流程分析
- 首先从event_list[i].data.ptr中获取发生事件的ngx_event_t结构
- 然后从nagx_event_t 结构中获取其关联的ngx_connection_t 结构
- 最后根据事件的不同类型,调用ngx_event_t 中对应的处理函数即可
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
int events;
struct epoll_event event_list[NGX_MAX_EVENTS];
// 调用 epoll_wait 等待事件发生
events = epoll_wait(ep, event_list, NGX_MAX_EVENTS, timer);
if (events == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_wait() failed");
return NGX_ERROR;
}
for (int i = 0; i < events; i++) {
ngx_event_t *ev;
ngx_connection_t *c;
// 获取发生事件的 ngx_event_t 结构
ev = event_list[i].data.ptr;
c = ev->data;
if (event_list[i].events & (EPOLLERR|EPOLLHUP)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "epoll_wait() error on fd:%d", c->fd);
}
if (event_list[i].events & EPOLLIN) {
ev->ready = 1;
// 调用事件处理函数
ev->handler(ev);
}
if (event_list[i].events & EPOLLOUT) {
ev->write = 1;
ev->handler(ev);
}
}
return NGX_OK;
}
Nginx处理读写事件流程分析(以HTTP请求为例)
处理写事件函数,通常是HTTP模块中的ngx_http_request_handler
void ngx_http_request_handler(ngx_event_t *ev) {
ngx_connection_t *c;
ngx_http_request_t *r;
ssize_t n;
ngx_buf_t *b;
// 从事件中获取连接
c = ev->data;
r = c->data;
// 检查是否有数据需要发送
b = r->out;
if (b != NULL) {
// 发送响应数据
n = c->send(c, b->pos, b->last - b->pos);
// 根据发送结果处理
if (n > 0) {
b->pos += n;
if (b->pos == b->last) {
// 数据发送完毕,清理缓冲区
ngx_http_finalize_request(r, NGX_HTTP_OK);
}
} else if (n == 0 || n == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
}
处理读事件,ngx_http_wait_request_handler
void ngx_http_wait_request_handler(ngx_event_t *ev) {
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_connection_t *hc;
ssize_t n;
ngx_buf_t *b;
ngx_http_core_srv_conf_t *cscf;
// 从事件中获取连接
c = ev->data;
r = c->data;
// 读取请求数据
n = c->recv(c, b->last, b->end - b->last);
// 根据读取结果处理
if (n > 0) {
b->last += n;
// 处理请求数据,例如解析HTTP请求头
ngx_http_process_request_line(r);
} else if (n == 0 || n == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
}
Nginx下其他多路复用机制了解
- ngx_epoll_module.c :epoll事件的处理模块,Linux使用,处理大量并发连接
- ngx_select_module.c :select事件处理模块,性能不然epoll
- ngx_poll_module.c:poll处理模块,介于select和epoll之间
- ngx_kqueue_module.c:基于kqueue的事件处理模块,主要是macOS系统使用
- ngx_eventport_module.c:Solaris特有的多路复用机制,提供了和epoll和kqueue类似的功能
- ngx_devpoll_module.c:Solaris特有的多路复用机制,改进poll