• nginx事件模块 第五篇 epoll add


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

    内容回顾

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

    ngx_epoll_module_ctx源码

     1static ngx_event_module_t  ngx_epoll_module_ctx = {
    2    &epoll_name,
    3    ngx_epoll_create_conf,               /* create configuration */
    4    ngx_epoll_init_conf,                 /* init configuration */
    5
    6    {
    7        ngx_epoll_add_event,             /* add an event */
    8        ngx_epoll_del_event,             /* delete an event */
    9        ngx_epoll_add_event,             /* enable an event */
    10        ngx_epoll_del_event,             /* disable an event */
    11        ngx_epoll_add_connection,        /* add an connection */
    12        ngx_epoll_del_connection,        /* delete an connection */
    13#if (NGX_HAVE_EVENTFD)
    14        ngx_epoll_notify,                /* trigger a notify */
    15#else
    16        NULL,                            /* trigger a notify */
    17#endif
    18        ngx_epoll_process_events,        /* process the events */
    19        ngx_epoll_init,                  /* init the events */
    20        ngx_epoll_done,                  /* done the events */
    21    }
    22};

    添加新事件

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

     1static ngx_int_t
    2ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
    3
    {
    4    int                  op;
    5    uint32_t             events, prev;
    6    ngx_event_t         *e;
    7    ngx_connection_t    *c;
    8    struct epoll_event   ee;
    9
    10    c = ev->data;
    11
    12    events = (uint32_t) event;
    13
    14    if (event == NGX_READ_EVENT) {
    15        e = c->write;
    16        prev = EPOLLOUT;
    17#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
    18        events = EPOLLIN|EPOLLRDHUP;
    19#endif
    20
    21    } else {
    22        e = c->read;
    23        prev = EPOLLIN|EPOLLRDHUP;
    24#if (NGX_WRITE_EVENT != EPOLLOUT)
    25        events = EPOLLOUT;
    26#endif
    27    }
    28
    29    if (e->active) {
    30        op = EPOLL_CTL_MOD;
    31        events |= prev;
    32
    33    } else {
    34        op = EPOLL_CTL_ADD;
    35    }
    36
    37#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP)
    38    if (flags & NGX_EXCLUSIVE_EVENT) {
    39        events &= ~EPOLLRDHUP;
    40    }
    41#endif
    42
    43    ee.events = events | (uint32_t) flags;
    44    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
    45
    46    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
    47        return NGX_ERROR;
    48    }
    49
    50    ev->active = 1;
    51    return NGX_OK;
    52}

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

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

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

    1 if (event == NGX_READ_EVENT) {
    2        e = c->write;
    3        prev = EPOLLOUT;
    4#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
    5        events = EPOLLIN|EPOLLRDHUP;
    6#endif
    7}

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

    1if (e->active) {
    2        op = EPOLL_CTL_MOD;
    3        events |= prev;
    4else {
    5        op = EPOLL_CTL_ADD;
    6}

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

     1 typedef union epoll_data {
    2     void        *ptr;
    3     int          fd;
    4     uint32_t     u32;
    5     uint64_t     u64;
    6epoll_data_t;
    7
    8struct epoll_event {
    9    uint32_t     events;      /* Epoll events */
    10    epoll_data_t data;        /* User data variable */
    11};

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

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


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

    郑尔多斯郑尔多斯
  • 相关阅读:
    一步一步制作jquery插件Tabs(ajax只请求一次效果,78行完成)
    javascript没那么简单
    jQuery插件制作备忘
    封装jQuery表格插件jqGrid,控件化jqGrid(二):显示
    shell脚本day01bash的基本特性
    pythonday01_语言元素
    pythonday01_环境搭建
    shell脚本作业
    [转]ADS简要教程
    极限挑战—C#+ODP 100万条数据导入Oracle数据库仅用不到1秒
  • 原文地址:https://www.cnblogs.com/zhengerduosi/p/10178530.html
Copyright © 2020-2023  润新知