nginx事件模組 -- 第五篇 epoll add

鄭爾多斯發表於2018-12-26

微信公眾號:鄭爾多斯
關注可瞭解更多的Nginx知識。任何問題或建議,請公眾號留言;
關注公眾號,有趣有內涵的文章第一時間送達!

內容回顧

上一篇文章我們介紹了Nginxepoll初始化過程。從這一篇文章開始我們繼續介紹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;
4else {
5        op = EPOLL_CTL_ADD;
6}
複製程式碼

因為往epoll中增加事件的時候,有兩種方式,分別為 addmodify。當我們將某個fd新增read event的時候,如果該fdwrite 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

這裡有一點要注意,那就是我們新增的eventdata欄位,我們先看一下epoll函式中event的結構:

 1 typedef union epoll_data {
2     void        *ptr;
3     int          fd;
4     uint32_t     u32;
5     uint64_t     u64;
6epoll_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 eventdata欄位是什麼時候指向了connection呢?
其實是在 ngx_get_connection()方法中。我們隨後會分析這個函式。


喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯

相關文章