微信公众号:
关注可了解更多的Nginx
知识。任何问题或建议,请公众号留言;关注公众号,有趣有内涵的文章第一时间送达!
内容回顾
上一篇文章我们介绍了Nginx
的epoll
初始化过程。从这一篇文章开始我们继续介绍ngx_epoll_module
的源码,包括添加事件,删除事件,触发事件等。
ngx_epoll_module_ctx源码
1static ngx_event_module_t ngx_epoll_module_ctx = { 2 &epoll_name, 3 ngx_epoll_create_conf, /* create configuration */ 4 ngx_epoll_init_conf, /* init configuration */ 5 6 { 7 ngx_epoll_add_event, /* add an event */ 8 ngx_epoll_del_event, /* delete an event */ 9 ngx_epoll_add_event, /* enable an event */ 10 ngx_epoll_del_event, /* disable an event */ 11 ngx_epoll_add_connection, /* add an connection */ 12 ngx_epoll_del_connection, /* delete an connection */ 13#if (NGX_HAVE_EVENTFD) 14 ngx_epoll_notify, /* trigger a notify */ 15#else 16 NULL, /* trigger a notify */ 17#endif 18 ngx_epoll_process_events, /* process the events */ 19 ngx_epoll_init, /* init the events */ 20 ngx_epoll_done, /* done the events */ 21 } 22}; 复制代码
添加新事件
从上面的源码中我们可以知道,epoll
添加事件的方法为ngx_epoll_add_event
,源码如下:
1static ngx_int_t 2ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) 3{ 4 int op; 5 uint32_t events, prev; 6 ngx_event_t *e; 7 ngx_connection_t *c; 8 struct epoll_event ee; 9 10 c = ev->data; 11 12 events = (uint32_t) event; 13 14 if (event == NGX_READ_EVENT) { 15 e = c->write; 16 prev = EPOLLOUT; 17#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP) 18 events = EPOLLIN|EPOLLRDHUP; 19#endif 20 21 } else { 22 e = c->read; 23 prev = EPOLLIN|EPOLLRDHUP; 24#if (NGX_WRITE_EVENT != EPOLLOUT) 25 events = EPOLLOUT; 26#endif 27 } 28 29 if (e->active) { 30 op = EPOLL_CTL_MOD; 31 events |= prev; 32 33 } else { 34 op = EPOLL_CTL_ADD; 35 } 36 37#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP) 38 if (flags & NGX_EXCLUSIVE_EVENT) { 39 events &= ~EPOLLRDHUP; 40 } 41#endif 42 43 ee.events = events | (uint32_t) flags; 44 ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); 45 46 if (epoll_ctl(ep, op, c->fd, &ee) == -1) { 47 return NGX_ERROR; 48 } 49 50 ev->active = 1; 51 return NGX_OK; 52} 复制代码
该函数的三个参数功能如下:
ev
:我们要添加的事件event
: 事件的类型,读事件或者写事件,我们这里分析read event
,对于write event
来说道理相同flags
: 添加事件的参数
我们这里只分析read event
:
read event
的时候要判断c->write
呢? 1 if (event == NGX_READ_EVENT) { 2 e = c->write; 3 prev = EPOLLOUT; 4#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP) 5 events = EPOLLIN|EPOLLRDHUP; 6#endif 7} 复制代码
其实结合后面的代码就很清楚了,我们看一下后面的代码:
1if (e->active) { 2 op = EPOLL_CTL_MOD; 3 events |= prev; 4} else { 5 op = EPOLL_CTL_ADD; 6} 复制代码
因为往epoll
中增加事件的时候,有两种方式,分别为 add
和 modify
。当我们将某个fd
添加read event
的时候,如果该fd
的write event
已经被添加到了epoll
中,那么我们就不能继续add
了,只能modify
,所以这里要先判断一下write event
的状态。
man epoll
手册,在Question and answers
部分有下面一个Question
,如下:
Q1
What happens if you register the same file descriptor on an epoll instance twice?A1
You will probably get EEXIST. However, it is possible to add a duplicate (dup(2), dup2(2), fcntl(2) F_DUPFD) descriptor to t
这里有一点要注意,那就是我们添加的event
的data
字段,我们先看一下epoll
函数中event
的结构:
1 typedef union epoll_data { 2 void *ptr; 3 int fd; 4 uint32_t u32; 5 uint64_t u64; 6} epoll_data_t; 7 8struct epoll_event { 9 uint32_t events; /* Epoll events */ 10 epoll_data_t data; /* User data variable */ 11}; 复制代码
在 ngx_epoll_add_event()
函数中有下面一句话:
1ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); 复制代码
这里会把 event->data->ptr
指向当前事件对应的connection
。这是一个很重要的特性。这样的话,当我们epoll_wait()
获取到某个事件之后,就可以拿到这个事件对应的connection
,然后进行各种操作。
这就是ngx_epoll_add_event()
的处理流程,这里遗留了一个问题:
read event
或者 write event
的data
字段是什么时候指向了connection
呢?其实是在 ngx_get_connection()
方法中。我们随后会分析这个函数。 喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达