微信公眾號:鄭爾多斯
關注可瞭解更多的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()
方法中。我們隨後會分析這個函式。
喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達