http://blog.youkuaiyun.com/brainkick/article/details/9080789
- static ngx_int_t
- ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
- {
- int op;
- uint32_t events, prev;
- ngx_event_t *e;
- ngx_connection_t *c;
- struct epoll_event ee;
-
- c = ev->data;
-
- events = (uint32_t) event;
-
- if (event == NGX_READ_EVENT) {
- e = c->write;
- prev = EPOLLOUT;
- } else {
- e = c->read;
- prev = EPOLLIN;
- }
-
- if (e->active) {
- op = EPOLL_CTL_MOD;
- events |= prev;
- } else {
- op = EPOLL_CTL_ADD;
- }
-
- ee.events = events | (uint32_t) flags;
- ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
-
-
- if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
- return NGX_ERROR;
- }
-
- ev->active = 1;
-
- return NGX_OK;
- }
为什么明明处理的是NGX_READ_EVENT,即所谓的读事件,为什么要去管c->write?即代码:
- if (event == NGX_READ_EVENT) {
- e = c->write;
- prev = EPOLLOUT;
- } else {
- e = c->read;
- prev = EPOLLIN;
- }
其实答案就跟在后面:
- if (e->active) {
- op = EPOLL_CTL_MOD;
- events |= prev;
- } else {
- op = EPOLL_CTL_ADD;
- }
结合这两段代码,意图就很明显了(明显吗?既然明显,为什么还有不少人在这里被绊住了呢?)。在epoll中监控读写事件时,主要通过接口epoll_ctl来处理。注意使用时有两种操作add和mod,当一个fd第一次注册到epoll时,使用add方式。而如果之前已经添加过对该fd读写事件的监控,就要通过mod来修改原来的监控方式,告知epoll我们的需求。如果一个fd被重复执行add会报错。
- man epoll:
- Q: What happens if you register the same file descriptor on an epoll
- instance twice?
-
- A: You will probably get EEXIST. However, it is possible to add a
- duplicate (dup(2), dup2(2), fcntl(2) F_DUPFD) descriptor to the
- same epoll instance. This can be a useful technique for filtering
- events, if the duplicate file descriptors are registered with dif-
- ferent events masks.
-
所以nginx这里就是为了避免这种情况,当要在epoll中加入对一个fd读事件(即NGX_READ_EVENT)的监听时,nginx先看一下与这个fd相关的写事件的状态,即e=c->write,如果此时e->active为1,说明该fd之前已经以NGX_WRITE_EVENT方式被加到epoll中了,此时只需要使用mod方式,将我们的需求加进去,否则才使用add方式,将该fd注册到epoll中。反之处理NGX_WRITE_EVENT时道理是一样的。