Nginx:事件模块
参考资料<深入理解Nginx>
根据不同的系统内核,Nginx会使用不同的事件驱动机制,本次描述的场景是使用epoll来驱动事件的处理。
epoll的使用方法
1.int epoll_create(int size);
epoll_create返回一个句柄,之后epoll的使用将依靠这个句柄来标识。参数size只是告诉epoll所要处理的大致事件数目,一些内核版本的实现中,这个参数没有任何意义。
2.int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epoll_ctl向epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回-1。
参数epfd是epoll_create方法返回的句柄,而op参数的意义如下表
参数fd是待检测的连接套接字,第四个参数是在告诉epoll对什么样的事件感兴趣,它使用的epoll_event结构体定义如下
struct epoll_event {
_uint32_t events;
epoll_data_t data;
};
events取值如下表
而data成员是一个epoll_data联合
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
这个data成员还与具体的使用方式相关。例如,ngx_epoll_module模块使用了给ptr成员,作为指向ngx_connection_t连接的指针。
3.int epoll_wait(int fd,struct epoll_event *events,int maxevents,int timeout);
第一个参数epfd是epoll的描述符。第二个参数events则是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中,
第三个参数表示本次可以返回的最大事件数目,第四个参数表示在没有检测到时间发生时最多等待的时间。
ngx_epoll_module模块
该模块使用如下的上下文结构
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 */
}
};
在Nginx的启动过程中。ngx_epoll_init方法将会被调用,它主要做了两件事情:
1.调用epoll_create方法创建epoll对象。
2.创建event_list数组,用于进行epoll_wait调用时传递内核态的事件。
ngx_epoll_add_event通过调用epoll_ctl向epoll中添加或者删除事件。可以通过这个函数的部分源码来了解一下该过程
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; //每个事件的data成员都存放着其对应的ngx_connection_t连接
c = ev->data; //确定当前时间是读事件还是写事件
events = (uint32_t) event; ...
//根据active标志位确定是否为活跃事件,以决定到底是修改还是添加事件
if (e->active) {
op = EPOLL_CTL_MOD;
events |= prev; } else {
op = EPOLL_CTL_ADD;
} ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); //调用epoll_ctl方法向epoll中添加事件
if (epoll_ctl(ep, op, c->fd, &ee) == -) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
} //标识为活跃事件
ev->active = ; return NGX_OK;
}
同理,ngx_epoll_del_event方法也通过类似的方式删除事件。
对于ngx_epoll_add_connection和ngx_epoll_del_connection方法,也是调用epoll_ctl进行处理,只是每一个连接都对应读/写事件。
ngx_epoll_process_events方法则调用epoll_wait来获取事件并且处理事件,下面是该函数的部分源码
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int events;
uint32_t revents;
ngx_int_t instance, i;
ngx_event_t *rev, *wev, **queue;
ngx_connection_t *c; //一开始就是等待事件,最长等待时间为timer
events = epoll_wait(ep, event_list, (int) nevents, timer); ...
//循环开始处理收到的所有事件
for (i = ; i < events; i++) {
//ptr成员指向的是ngx_connection_t,但最后一位有特殊含义,需要将它屏蔽掉
c = event_list[i].data.ptr;
instance = (uintptr_t) c & ;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~); //取出读事件
rev = c->read;
...
//取得发生一个事件
revents = event_list[i].events; ...
//该事件是一个读事件而且是活跃的
if ((revents & EPOLLIN) && rev->active) { ...
//如果设置了NGX_POST_EVENTS标识,事件放入到相应的队列中
if (flags & NGX_POST_EVENTS) {
queue = (ngx_event_t **) (rev->accept ?
&ngx_posted_accept_events : &ngx_posted_events); ngx_locked_post_event(rev, queue);
//否则立刻处理
} else {
rev->handler(rev);
}
} //获取写事件
wev = c->write; if ((revents & EPOLLOUT) && wev->active) { ...
if (flags & NGX_POST_EVENTS) {
ngx_locked_post_event(wev, &ngx_posted_events); } else {
wev->handler(wev);
}
}
}
...
return NGX_OK;
}
ngx_process_events_and_timers流程
在Nginx中,每个worker进程都在ngx_worker_process_cycle方法中循环处理事件。
处理分发事件实际上就是调用的ngx_process_events_and_timers方法,循环调用该函数就是在处理所有的事件,该方法的核心的操作主要有以下3个:
1.调用所使用的事件驱动模块实现的process_events方法,处理网络事件(调用ngx_epoll_process_events方法)。
2.处理两个post事件队列中的事件(调用ngx_event_process_posted方法)。
3.处理定时器时间(调用ngx_event_expire_timers方法)
下图展示了该方法的流程,结合前面的进行理解
事实上,在调用上面循环的方法之前ngx_event_core_module模块会做一些初始化工作:
1.预分配cycle->connection数组充当连接池
2.预分配所有写事件到cycle->read_events数组
3.预分配所有读事件到cycle->write_events数组
(Nginx中连接池跟读写事件都是这个阶段预先分配好的,其大小跟配置项有关)
4.为所有ngx_listening_t监听对象中的connectin成员分配连接,同时对监听端口的读事件设置处理方法为ngx_event_accept(最后有这个方法的介绍)
5.将监听对象连接的读事件添加到事件驱动模块中,这样,epoll等事件模块就开始检测监听服务,并开始向用户提供服务了。
建立新连接
处理新连接事件的回调函数是ngx_event_accept,其原型如下
void ngx_event_accept(ngx_event_t *ev);
下图展示了该方法的流程
Nginx:事件模块的更多相关文章
- nginx事件模块分析(一)
nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...
- 【Nginx】Nginx事件模块
一.事件处理框架概述 事件处理框架所要解决的问题是如何收集.管理.分发事件.事件以网络事件和定时器事件为主,而网络事件中以TCP网络事件为主.事件处理框架需要在不同的操作系统内核中选择一种事件驱动机制 ...
- Nginx功能模块汇总
主要文档 Nginx功能概述.为什么选择Nginx.Nginx安装.常见问题(FAQ).配置符号参考.调试 nginx.优化 Nginx.运行和控制Nginx 核心模块 Nginx事件模块.Nginx ...
- 高性能Web服务器Nginx的配置与部署研究(8)核心模块之事件模块
一.事件模块的作用是什么? 用来设置Nginx处理链接请求. 二.相关指令 1. accept_mutex 含义:设置是否使用连接互斥锁进行顺序的accept()系统调用. 语法:accept_mut ...
- 【Nginx】ngx_event_core_module事件模块
功能:创建连接池,决定使用哪些事件驱动机制,以及初始化将要使用的事件模块 该模块定义了ngx_event_core_commands数组处理其感兴趣的7个配置项 ngx_event_conf_t为该模 ...
- nginx源代码分析--事件模块 & 琐碎
通过HUP信息使得NGINX实现又一次读取配置文件,使用USR2信号使得NGINX实现平滑升级. 在nginx中有模块这么一说,对外全部的模块都是ngx_module_t类型,这个结构体作为全部模块的 ...
- nginx日志模块、事件模块
日志模块 1.access_log指令 语法: access_log path [format [buffer=size [flush=time]]]; access_log logs/access. ...
- Nginx事件管理之ngx_event_core_module模块
1. 概述 ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一位.它主要完成以下两点任务: 创建连接池(包括读/写事件): 决定究竟使用哪些事件驱动机 ...
- Nginx事件管理之核心模块ngx_events_module
1. ngx_events_module核心模块的功能介绍 ngx_events_module 模式是一个核心模块,它的功能如下: 定义新的事件类型 定义每个事件模块都需要实现的ngx_event_m ...
随机推荐
- table 实现 九宫格布局
九宫格布局 最近遇到一个题目,是实现一个九宫格布局的.实现的效果大概是下图这种这样子的: (鼠标悬浮的时候,九宫格的边框颜色是改变的.) 首先想到的是直接使用<table>进行布局,原因很 ...
- 内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages【转】
转自:http://www.cnblogs.com/yfz0/p/5829443.html 在内核模块中申请分配内存需要使用内核中的专用API:kmalloc.vmalloc.kzalloc.kcal ...
- Linux文件查找命令find,xargs详述【转】
转自:http://blog.csdn.net/cxylaf/article/details/4069595 转自http://www.linuxsir.org/main/?q=node/137 Li ...
- visio画任意形状图形
1,连接线--右击---曲线连接线 2,选中组合 3,开发工具--操作--连接--填充
- PHP将两个二维数组合并为一个二维数组的方法
$a = array(0 => Array(id => 66,class_name => math),1 => Array(id => 67,class_name =&g ...
- AC日记——Little Elephant and Problem codeforces 221c
221C 思路: 水题: 代码: #include <cstdio> #include <cstring> #include <iostream> #include ...
- vi / vim 设置
一.vi下方向键输入后,出现ABCD,解决方法: 在vi中输入:set nocp 按回车即可. 二.设置TAB缩进4个空格: 为了vim更好的支持python写代码,修改tab默认4个空格有两种设置方 ...
- 牛客网 暑期ACM多校训练营(第二场)J.farm-STL(vector)+二维树状数组区间更新、单点查询 or 大暴力?
开心.jpg J.farm 先解释一下题意,题意就是一个n*m的矩形区域,每个点代表一个植物,然后不同的植物对应不同的适合的肥料k,如果植物被撒上不适合的肥料就会死掉.然后题目将每个点适合的肥料种类( ...
- navicat连接MySQL8.0.11提示2059错误
错误原因:mysql加密规则的改变: mysql加密规则:mysql_native_password mysql8之前的版本 caching_sha2_password mysq ...
- android如何取消闹铃
取消闹钟: Intent intent = new Intent(context, TestReceiver.class); PendingIntent pi = PendingIntent.getB ...