ngx_epoll_module模块是事件驱动模块的一种,下面主要来分析一下这个模块的接口以及时如何与epoll结合起来的
首先来看一下模块的相关结构体
static ngx_str_t epoll_name = ngx_string("epoll");
static ngx_command_t ngx_epoll_commands[] = {
{ ngx_string("epoll_events"),//在调用epoll_wait时,将由第二和第三个参数告诉linux内核一次可以返回多少个事件,这个配置项表示调用一次最多//返回多少事件
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_epoll_conf_t, events),
NULL },
{ ngx_string("worker_aio_requests"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_epoll_conf_t, aio_requests),
NULL },
ngx_null_command
};
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
{ //这个actions成员是定义事件驱动模块的核心方法
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 */
NULL, /* process the changes */
ngx_epoll_process_events, /* process the events */ //此函数在解析worker的文章中有过详解
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
ngx_module_t ngx_epoll_module = {
NGX_MODULE_V1,
&ngx_epoll_module_ctx, /* module context */
ngx_epoll_commands, /* module directives */
NGX_EVENT_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
};
下面是ngx_epoll_conf_t结构体
typedef struct {
ngx_uint_t events;
ngx_uint_t aio_requests;
} ngx_epoll_conf_t;
在调用ngx_event_core_module模块时会在启动时调用ngx_evennt_process_init函数,这个函数会检查配置项结构,得到使用哪一个事件驱动模块,并且调用相应模块的初始化函数。在ngx_epoll_module中,这个函数是ngx_epoll_init
下面来分析代码
主要做了两件事
1.调用epoll_create方法创建epoll对象
1.创建epoll_list数组,用于进行epoll_wait调用中接受事件参数
static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_epoll_conf_t *epcf;
//取得epoll模块的配置结构
epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
//ep是epoll模块定义的一个全局变量,初始化为-1
if (ep == -1) {
/*
调用epoll_create 在内核中创建epoll对象。
*/
ep = epoll_create(cycle->connection_n / 2); //创一个epoll对象,容量为总连接数的一半(具体数目并无影响)
if (ep == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"epoll_create() failed");
return NGX_ERROR;
}
}
if (nevents < epcf->events) {
if (event_list) {
ngx_free(event_list);
}
//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;//设置ngx_event_actions借口
#if (NGX_HAVE_CLEAR_EVENT)
ngx_event_flags = NGX_USE_CLEAR_EVENT //epoll添加这个标志,主要为了实现边缘触发
#else
ngx_event_flags = NGX_USE_LEVEL_EVENT //水平触发
#endif
|NGX_USE_GREEDY_EVENT //io的时候,知道EAGAIN为止
|NGX_USE_EPOLL_EVENT; //epoll标志
return NGX_OK;
}
下面分析ngx_epoll_add_event()函数,了解他是如何调用epoll_ctl向epoll中添加或者删除事件的
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;//每个事件的data成员都存放着ngx_connection_t连接
events = (uint32_t) event;//根据当前事件是读事件还是写事件来决定参数是EPOLLIN还是EPOLLOUT
if (event == NGX_READ_EVENT) {
e = c->write;
prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN)
events = EPOLLIN;
#endif
} else {
e = c->read;
prev = EPOLLIN;
#if (NGX_WRITE_EVENT != EPOLLOUT)
events = EPOLLOUT;
#endif
}
if (e->active) {//根据判断当前事件是否活跃决定是修改还是添加事件
op = EPOLL_CTL_MOD;
events |= prev;
} else {
op = EPOLL_CTL_ADD;
}
ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"epoll add event: fd:%d op:%d ev:%08XD",
c->fd, op, ee.events);
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 = 1;//将当前事件设置为活跃
return NGX_OK;
}
ngx_epoll_process_events在之前曾经分析过,在调用ngx_process_timers_and_events时我们会调用ngx_epoll_process_events函数来收集已经就绪的事件,并且在使用accept_mutex锁时将事件分为两个连链表,accept事件链表和post事件链表,在后面将优先依次调用accept事件链表中的事件的回调函数(ngx_event_accept),然后释放锁再依次调用post事件链表中事件的回调函数
下面分析一下函数ngx_event_accept
//这个函数被调用是当listen 句柄有可读事件之后才被调用
void
ngx_event_accept(ngx_event_t *ev)
{
socklen_t socklen;
ngx_err_t err;
ngx_log_t *log;
ngx_socket_t s;
ngx_event_t *rev, *wev;
ngx_listening_t *ls;
ngx_connection_t *c, *lc;
ngx_event_conf_t *ecf;
u_char sa[NGX_SOCKADDRLEN];
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
ev->available = 1;
} else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"accept on %V, ready: %d", &ls->addr_text, ev->available);
do {
socklen = NGX_SOCKADDRLEN;
//开始accept句柄
#if (NGX_HAVE_ACCEPT4)
if (use_accept4) {
s = accept4(lc->fd, (struct sockaddr *) sa, &socklen,
SOCK_NONBLOCK);
} else {
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
}
#else
s = accept(lc->fd, (struct sockaddr *) sa, &socklen); // accept一个新的连接
#endif
if (s == -1) { //连接的错误处理
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"accept() not ready");
return;
}
//accept到一个新的连接以后,就重新计算ngx_accept_disabled的值。它主要用来做负载均衡使用
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
//从连接池取得连接,然后创建连接里面包含的数据结构
c = ngx_get_connection(s, ev->log);
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
return;
}
//创建内存池
c->pool = ngx_create_pool(ls->pool_size, ev->log);
if (c->pool == NULL) {
ngx_close_accepted_connection(c);
return;
}
//分配客户端地址
c->sockaddr = ngx_palloc(c->pool, socklen);
if (c->sockaddr == NULL) {
ngx_close_accepted_connection(c);
return;
}
ngx_memcpy(c->sockaddr, sa, socklen);
//分配log
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_close_accepted_connection(c);
return;
}
/* set a blocking mode for aio and non-blocking mode for others */
if (ngx_inherited_nonblocking) {
if (ngx_event_flags & NGX_USE_AIO_EVENT) {
if (ngx_blocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_blocking_n " failed");
ngx_close_accepted_connection(c);
return;
}
}
} else {
if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) {
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_nonblocking_n " failed");
ngx_close_accepted_connection(c);
return;
}
}
}
*log = ls->log;
//设置读取的回调,这里依赖于操作系统
c->recv = ngx_recv;
c->send = ngx_send;
c->recv_chain = ngx_recv_chain;
c->send_chain = ngx_send_chain;
c->log = log;
c->pool->log = log;
//设置client的ip地址
c->socklen = socklen;
c->listening = ls;
c->local_sockaddr = ls->sockaddr;
c->unexpected_eof = 1;
//准备设置读写的结构
rev = c->read;
wev = c->write;
wev->ready = 1;
//这里使用的epoll模型,在这里设置连接为nonblocking
if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {
/* rtsig, aio, iocp */
rev->ready = 1;
}
if (ev->deferred_accept) {
rev->ready = 1;
}
//设置log
rev->log = log;
wev->log = log;
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
if (ngx_add_conn(c) == NGX_ERROR) {
ngx_close_accepted_connection(c);
return;
}
}
log->data = NULL;
log->handler = NULL;
//它将完成新连接的最后初始化工作,同时将accept到的新连接放入epoll中,挂在handler上的函数就是ngx_http_init_connection
ls->handler(c);// 被初始化为 ngx_http_init_connection
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available--;
}
} while (ev->available);
}
此函数执行流程大致如下:
1.调用accept方法建立新连接,如果没有准备好的新连接,ngx_event_accept方法直接放回
2.设置负载均衡阀值ngx_accept_disabled,这个阀值是进程允许的总连接数的1/8减去空闲连接数
3.在连接池中获取一个ngx_connection_t结构体
4.为ngx_connection_t中的pool指针建立内存池,在这个连接释放到控线连接池时释放pool内存池
5,将这个新连接的对应读事件添加到epoll中,连接上如果收到用户请求epoll_wait就会调用相应函数(ngx_http_init_connection_t)
6.调用监听对象ngx_listening_t中的handler回调方法。ngx_listening_t结构体的handler回调方法就是当新的TCP连接刚刚建立完成时就在这里调用