nginx中的事件响应机制(以epoll为例)

  • Post author:
  • Post category:其他


一.nginx中提供的事件驱动如何实现?

在sys/epoll.h中

1、epoll_create函数

函数声明:int epoll_create(int size)

该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同

2、epoll_ctl函数

函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。

3、epoll_wait函数

函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)

该函数用于轮询I/O事件的发生;

在ngx_epoll_epoll_module.c中的函数ngx_epoll_add_event等调用以上几个函数来将文件描述符事件添加到epoll事件监听中去(为何在nginx中有一个空的epoll_ctl等函数???)。

那么这个添加的过程是怎么走的呢,nginx如何将文件描述符事件加入到epoll事件监听中去的呢?

如下这样处理

在Ngx_epoll_module.c中定义一个事件处理模块结构

typedef struct {
//事件模块 的名称
    ngx_str_t              *name;
//在解析配置前,这个回调方法用于创建存储配置项参数的结构体
    void                 *(*create_conf)(ngx_cycle_t *cycle);
//在解析配置项后,init_conf方法会被调用,用以综合处理当前事件模块感兴趣的所有配置项
    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);
//对于事件驱动机制,每个事件模块需要实现的10个抽象方法
    ngx_event_actions_t     actions;
} ngx_event_module_t;

其中ngx_event_actions_t结构体的定义如下:
typedef struct {
//添加事件方法,负责将感兴趣的事件添加到操作系统提供的事件驱动机制中(如epoll,kqueue)
//这样,在事件发生后,可以调用下面的process_events时或取这个事件
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
删除事件方法,将已存在于事件驱动机制中的事件移除,以后再发生这样的事件
调用process_events也无法获取事件
*/
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
启用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该事件的实现
都是与上面的add方法一致
*/
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
禁用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该事件的实现
都是与上面的del方法一致
*/
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
//向事件驱动机制中添加一个连接,这意味着连接上的读写事件都添加到事件驱动机制中
    ngx_int_t  (*add_conn)(ngx_connection_t *c);
//从事件驱动中移除一个连接的读写事件
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
//在正常的工作中,将通过调用这个函数来处理事件,
//在ngx_process_events_and_timers方法中调用,它是处理分发事件的核心
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                   ngx_uint_t flags);
//初始化事件驱动模块
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
//退出事件驱动模块前调用的方法
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;

然后用这个类型定义一个变量来添加这些处理函数

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 */
        NULL,                            /* process the changes */
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};

在ngx_event_core_module核心的事件模块中有如下定义

ngx_module_t  ngx_event_core_module = {
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx,            /* module context */
    ngx_event_core_commands,               /* module directives */
    NGX_EVENT_MODULE,                      /* module type */
    NULL,                                  /* init master */
    ngx_event_module_init,                 /* init module */
    ngx_event_process_init,                /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

其中ngx_event_process_init函数是在初始化进程时候调用

1.在ngx_event_process_init这个函数中,使用了如下语句

module->actions.init(cycle, ngx_timer_resolution)用这个来调用ngx_epoll_module_ctx 这个结构中定义init来调用ngx_epoll_init这个回调函数

在ngx_epoll_init这个函数中:通过ngx_event_actions = ngx_epoll_module_ctx.actions;将这些回调函数赋给全局变量ngx_event_actions.

2.在ngx_event_process_init这个函数中,还使用ngx_add_event(rev, 0, NGX_IOCP_ACCEPT),通过宏定义#define ngx_add_event ngx_event_actions.add等来将文件描述符的事件加入到epoll事件监听中去。

事件发生后,nginx如何处理事件?

process_events 在epoll模式中所对应的ngx_epoll_process_events函数是事件发生后处理的一个函数

处理过程基本是判断事件类别,如if(revents & EPOLLOUT) && wev->active),再根据不同的消费模块调用不同的处理函数wev->handler。

例如

http模块中,通过ngx_http_commands中设置的ngx_http_block函数

ngx_http_block函数中调用ls->handler = ngx_http_init_connection;设置ngx_listening_s监听的处理函数。

ngx_http_init_connection;函数中再调用rev->handler = ngx_http_init_request; c->write->handler = ngx_http_empty_handler;来设置文件描述符的处理函数,分别是ngx_http_init_request读事件函数和ngx_http_empty_handler写事件函数

在添加ngx_connection_s的文件描述符的事件监听过程,添加之后listening监听的事件是否被一同添加?(如何测试?)



版权声明:本文为qq_35039122原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。