static ngx_int_tngx_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;}This is the function that has been asked for countless times in the group. Before discussing it, let's take a look at the usage of this feature (active and ready. Then, let's look at this function. The main problem is that at the beginning of the function:
Why is ngx_read_event clearly handled, that is, the so-called read event? Why do I need to manage C-> write? Code:
if (event == NGX_READ_EVENT) { e = c->write; prev = EPOLLOUT;} else { e = c->read; prev = EPOLLIN;}The answer follows:
if (e->active) { op = EPOLL_CTL_MOD; events |= prev;} else { op = EPOLL_CTL_ADD;}By combining the two pieces of code, the intention is obvious (obviously? Obviously, why are many other people suspended here ?). When read/write events are monitored in epoll, The epoll_ctl interface is used for processing. Note that there are two ways to use add and mod. When an FD is registered with epoll for the first time, the add method is used. If you have already added monitoring for this FD read/write event, you need to use MoD to modify the original monitoring method and inform epoll of our needs. If an FD is repeatedly added, an error is returned.
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.
Therefore, nginx is used to avoid this situation. When you want to add a listener for an FD read event (ngx_read_event) to epoll, nginx first looks at the write event status related to this FD, that is, E = C-> write. If e-> active is 1 at this time, this indicates that the FD has been added to epoll in ngx_write_event mode. In this case, we only need to add our requirements in mod mode. Otherwise, the add method is used, register the FD to epoll. The principle is the same when processing ngx_write_event.