对于不须要加入到 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》