一.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监听的事件是否被一同添加?(如何测试?)