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

微信公众号:郑尔多斯
关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!

内容回顾

上一篇文章我们介绍了Nginxepoll初始化过程。从这一篇文章开始我们继续介绍ngx_epoll_module的源码,包括添加事件,删除事件,触发事件等。

ngx_epoll_module_ctx源码

static ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */

    {
        ngx_epoll_add_event,             /* add an event */
        ngx_epoll_del_event,             /* delete an event */
        ngx_epoll_add_event,             /* enable an event */
        ngx_epoll_del_event,             /* disable an event */
        ngx_epoll_add_connection,        /* add an connection */
        ngx_epoll_del_connection,        /* delete an connection */
#if (NGX_HAVE_EVENTFD)
        ngx_epoll_notify,                /* trigger a notify */
#else
        NULL,                            /* trigger a notify */
#endif
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};

添加新事件

从上面的源码中我们可以知道,epoll添加事件的方法为ngx_epoll_add_event,源码如下:

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;
#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
        events = EPOLLIN|EPOLLRDHUP;
#endif

    } else {
        e = c->read;
        prev = EPOLLIN|EPOLLRDHUP;
#if (NGX_WRITE_EVENT != EPOLLOUT)
        events = EPOLLOUT;
#endif
    }

    if (e->active) {
        op = EPOLL_CTL_MOD;
        events |= prev;

    } else {
        op = EPOLL_CTL_ADD;
    }

#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP)
    if (flags & NGX_EXCLUSIVE_EVENT) {
        events &= ~EPOLLRDHUP;
    }
#endif

    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;
}

该函数的三个参数功能如下:

ev:我们要添加的事件
event: 事件的类型,读事件或者写事件,我们这里分析read event,对于write event来说道理相同
flags: 添加事件的参数

我们这里只分析read event:
这里有一个问题,为什么添加read event的时候要判断c->write呢?

 if (event == NGX_READ_EVENT) {
        e = c->write;
        prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
        events = EPOLLIN|EPOLLRDHUP;
#endif
}

其实结合后面的代码就很清楚了,我们看一下后面的代码:

if (e->active) {
        op = EPOLL_CTL_MOD;
        events |= prev;
} else {
        op = EPOLL_CTL_ADD;
}

因为往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的结构:

 typedef union epoll_data {
     void        *ptr;
     int          fd;
     uint32_t     u32;
     uint64_t     u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};

ngx_epoll_add_event()函数中有下面一句话:

ee.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()方法中。我们随后会分析这个函数。


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
郑尔多斯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值