對於不須要加入到 post
隊列 延後處理的事件,nginx
的事件都是經過 ngx_epoll_process_events
函數進行處理的html
舉例:假如 epoll_wait
一次性返回 3 個事件,在第一個事件關閉了一個鏈接對應的正好是第三個事件的鏈接,第二個事件 accept
了一個鏈接,正好使用的是第二個事件的文件描述符nginx
如圖所示:git
那麼若是僅僅判斷是否使用的同一個描述符或者描述符是否被置爲 -1,就不能判斷是不是同一個鏈接github
上面的這個問題,稱之爲事件過時問題
tcp
nginx
是如何處理過時事件的?nginx
中的指針的最後一位必定是 0 ,因而,nginx
就使用這最後一位用來表示是否過時函數
深刻理解nginx中,第9章中有一句:利用指針的最後一位必定是0的特性。能解釋一下這個特性?post
看下面代碼中取出與判斷 instance
位的操做ui
nginx
會在每次 accept
一個鏈接的時候,將 instance
位取反,那麼只須要判斷 instance
位是否一直就能判斷事件是否過時了this
// nginx-1.9.2/src/core/ngx_connection.c // ngx_get_connection instance = rev->instance; ngx_memzero(rev, sizeof(ngx_event_t)); ngx_memzero(wev, sizeof(ngx_event_t)); rev->instance = !instance; wev->instance = !instance
// nginx-1.9.2/src/event/modules/ngx_epoll_module.c // ngx_epoll_process_events //遍歷本次epoll_wait返回的全部事件 for (i = 0; i < events; i++) { //和ngx_epoll_add_event配合使用 /* 對照着上面提到的ngx_epoll_add_event方法,能夠看到ptr成員就是ngx_connection_t鏈接的地址,但最後1位有特殊含義,須要把它屏蔽掉 */ c = event_list[i].data.ptr; //經過這個肯定是那個鏈接 instance = (uintptr_t) c & 1; //將地址的最後一位取出來,用instance變量標識, 見ngx_epoll_add_event /* 不管是32位仍是64位機器,其地址的最後1位確定是0,能夠用下面這行語句把ngx_connection_t的地址還原到真正的地址值 */ //注意這裏的c有多是accept前的c,用於檢測是否客戶端發起tcp鏈接事件,accept返回成功後會從新建立一個ngx_connection_t,用來讀寫客戶端的數據 c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); rev = c->read; //取出讀事件 //注意這裏的c有多是accept前的c,用於檢測是否客戶端發起tcp鏈接事件,accept返回成功後會從新建立一個ngx_connection_t,用來讀寫客戶端的數據 if (c->fd == -1 || rev->instance != instance) { //判斷這個讀事件是否爲過時事件 //當fd套接字描述符爲-l或者instance標誌位不相等時,表示這個事件已通過期了,不用處理 /* * the stale event from a file descriptor * that was just closed in this iteration */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll: stale event %p", c); continue; } // 還有代碼,可是不貼這麼多了 ...... }
那麼你可能也跟我有同樣的疑問,若是是下面兩種情形怎麼辦?debug
這樣 instance
位又成了 1,那 e
事件處理的豈不是 3 鏈接的事件了,這樣過時事件並無解決啊
首先,第二張圖片中的狀況不可能存在,由於 epoll
中的事件是有順序的,c
事件必然是再 e
事件以後
那麼第一張圖片中的狀況仍是沒有解決過時事件啊
因而我就翻閱不少資料(主要靠百度)
看到有人遇到過這個疑問:
若是以爲上面文章太長能夠看個人講解:
意思呢就是
nginx
accept
鏈接以後,會馬上將鏈接放到 post
延遲處理隊列中,不會出現 accept
以後馬上 close
的狀況
因而呢,nginx
就完美的解決了事件過時的狀況
《深刻理解 Nginx》