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