nginx源代码分析--event事件驱动初始化
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);//
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
ngx_conf_param(&conf)
enum {
parse_file = 0,
parse_block,
parse_param
} type;
rc = ngx_conf_read_token(cf); //读取token
/* 返回值:
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/
rc = ngx_conf_handler(cf, rc); //处理token
.............
cmd = ngx_modules[i]->commands;
.........
rv = cmd->set(cf, cmd, conf);
................
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;
............
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
.............
};
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
....................
};
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
static ngx_command_t ngx_events_commands[] = {
{ ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
........
};
//将cmd指向command
cmd = ngx_modules[i]->commands;
//回调函数,对每一个模块进行详细配置
rv = cmd->set(cf, cmd, conf);
static char *
ngx_events_block(ngx_conf_t *cf , ngx_command_t *cmd, void *conf)
{
char * rv;
void *** ctx;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_event_module_t * m; if (*(void **) conf) {
return "is duplicate" ;
} /* count the number of the event modules and set up their indices */
//初始化全部事件模块的ctx_index
ngx_event_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
} ngx_modules[ i]->ctx_index = ngx_event_max_module++;
} //创建配置须要的结构体空间
ctx = ngx_pcalloc(cf ->pool, sizeof( void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
} * ctx = ngx_pcalloc(cf ->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
} *( void **) conf = ctx; //调用全部事件模块的create_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
} m = ngx_modules[i ]->ctx; if (m ->create_conf) {
(* ctx)[ngx_modules[i ]->ctx_index] = m->create_conf (cf-> cycle);
if ((*ctx )[ngx_modules[i]-> ctx_index] == NULL ) {
return NGX_CONF_ERROR;
}
}
} pcf = *cf ;
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF; //为模块event全部模块解析配置
rv = ngx_conf_parse(cf , NULL); * cf = pcf ; if (rv != NGX_CONF_OK)
return rv ; //为event全部模块调用init_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
} m = ngx_modules[i ]->ctx; if (m ->init_conf) {
rv = m ->init_conf( cf->cycle , (*ctx)[ngx_modules[ i]->ctx_index ]);
if (rv != NGX_CONF_OK) {
return rv ;
}
}
} return NGX_CONF_OK;
}
static ngx_command_t ngx_event_core_commands[] = {
//连接池的大小,即每一个worker进程中支持的最大连接数
//他与以下的connections配置项的意义是反复的
{ ngx_string("worker_connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
//连接池的大小,与上一项配置反复
{ ngx_string("connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
//确定哪一个事件模块作为事件驱动机制
{ ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
//对于epoll事件驱动模式来说,当接收一个新链接事件时候,
//调用accept尽可能多的接收连接
{ ngx_string("multi_accept"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, multi_accept),
NULL },
//确定是否使用负载均衡锁,默认开启
{ ngx_string("accept_mutex"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex),
NULL },
//启动负载均衡锁以后,延迟accept_mutex_delay毫秒以后再进行连接
{ ngx_string("accept_mutex_delay"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex_delay),
NULL },
//对于来自指定ip的连接须要打印debug级别的日志.
{ ngx_string("debug_connection"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_debug_connection,
0,
0,
NULL },
ngx_null_command
};
typedef struct {
//连接池大小
ngx_uint_t connections;
//选用的事件模块在全部事件模块的编号,即ctx_index
ngx_uint_t use;
//标志位,若为1,则在接收一个连接事件时候,一次性尽可能建立多个连接
ngx_flag_t multi_accept;
//标志位,为1时候採用负载均衡锁
ngx_flag_t accept_mutex;
//负载均衡锁会使有些worker进程在拿不到锁的时候延迟一段时间
//这段时间就是用accept_mutex_delay表示的
ngx_msec_t accept_mutex_delay;
//所选用的事件模块的名字,他与use是匹配的.
u_char *name;
} ngx_event_conf_t;
ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
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
};
//调用全部模块的init_module
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_module) {
if (ngx_modules[i]->init_module(cycle) != NGX_OK) {
/* fatal */
exit(1);
}
}
}
//src/event/ngx_event.c
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
void ***cf;
u_char *shared;
size_t size, cl;
ngx_shm_t shm;
ngx_time_t *tp;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf; //推断ngx_events_module是否调用过初始化conf操作
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module); if (cf == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"no \"events\" section in configuration");
return NGX_ERROR;
} //获取ngx_event_core_module模块的配置结构
ecf = (*cf)[ngx_event_core_module.ctx_index]; //查看是否是event中的模块,比如use 。。。。
if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"using the \"%s\" event method", ecf->name);
}
//获取ngx_core_module模块的配置结构
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); //从ngx_core_module模块的配置结构中获取timer_resolution參数
ngx_timer_resolution = ccf->timer_resolution; #if !(NGX_WIN32)
{
ngx_int_t limit;
struct rlimit rlmt; //获取当前进程可以打开的最大文件数 man getrlimit
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed, ignored"); } else {
//假设ngx_event_core_module模块连接数大于当前(软)限制
//而且ngx_core_module最大连接数无限制
//或者ngx_event_core_module连接数大于ngx_core_module最大连接数
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
{
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile; ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"%ui worker_connections are more than "
"open file resource limit: %i",
ecf->connections, limit);
}
}
}
#endif /* !(NGX_WIN32) */ //假设关闭了master进程,就返回
//由于关闭了master进程就是单进程工作方式,
//之后的操作时创建共享内存实现锁等工作,单进程不须要。
if (ccf->master == 0) {
return NGX_OK;
} //假设已经存在accept相互排斥体了,不须要再反复创建了
if (ngx_accept_mutex_ptr) {
return NGX_OK;
} /* cl should be equal or bigger than cache line size */ cl = 128;
//这里创建size大小的共享内存,这块共享内存将被均分成三段
size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */ //准备共享内存,大小为size,命名nginx_shared_zone,
shm.size = size;
shm.name.len = sizeof("nginx_shared_zone");
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log; //创建共享内存,起始地址保存在shm.addr
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
//获取起始地址保存
shared = shm.addr; //accept相互排斥体取得共享内存的第一段cl大小内存
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
ngx_accept_mutex.spin = (ngx_uint_t) -1;
/*创建accept相互排斥体 accept相互排斥体的实现依赖是否支持原子操作,假设有对应的原子操作;
就是用取得的这段共享内存来实现accept相互排斥体;否则,将使用文件锁
来实现accept相互排斥体。 accept相互排斥体的作用是:避免惊群和实现worker进程的负载均衡。 */
if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
} //获取内存的第二段cl大小的地址
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"counter: %p, %d",
ngx_connection_counter, *ngx_connection_counter);
//获取内存的第三段cl大小的地址
ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl); tp = ngx_timeofday(); ngx_random_number = (tp->msec << 16) + ngx_pid; return NGX_OK;
}
//src/event/ngx_event.c
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
ngx_uint_t m, i;
ngx_event_t *rev, *wev;
ngx_listening_t *ls;
ngx_connection_t *c, *next, *old;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
ngx_event_module_t *module; //和之前一样,获取响应模块的配置结构
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); //master进程打开,worker进程大于1,已经创建了accept_mutex
//才打开accept相互排斥体
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1; //使用相互排斥体
ngx_accept_mutex_held = 0; //是否获得accept相互排斥体
ngx_accept_mutex_delay = ecf->accept_mutex_delay;//争抢相互排斥体失败后,等待下次争抢时间间隔 } else {
ngx_use_accept_mutex = 0;
} #if (NGX_THREADS)
//线程先不讲
#endif
//初始化计数器,此处将会创建一颗红黑树,来维护计时器,之后会详细解说
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
return NGX_ERROR;
} for (m = 0; ngx_modules[m]; m++) {
//这里之前讲过,跳过非NGX_EVENT_MODULE模块
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
//非use配置指令指定的模块跳过,linux默认epoll
if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
} module = ngx_modules[m]->ctx;
/*调用详细时间模块的init函数 因为nginx实现了非常多事件模块,比方:epoll、poll、select、dqueue、aio
(这些模块位于src/event/modules文件夹中),所以nginx对时间模块进行了一层抽象,
方便了不同的系统使用不同的事件模型,也便于扩展新的时间模型,我们的重点应该
放在epoll上。 此处的init回调,事实上就是调用了ngx_epoll_init函数。module->actions结构封装了
epoll的全部接口函数。nginx就是通过actions结构将epoll注冊到事件抽象层中。
actions的类型是ngx_event_action_t,位于src/event/ngx_event.h 这些详细的内容会在下一节中重点解说。 */
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
} break;
}
//此处省略部分内容
//创建全局的ngx_connection_t数组,保存全部的connection
//因为这个过程是在各个worker进程中运行的,所以每一个worker都有自己的connection数组
cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
} c = cycle->connections; //创建一个读事件数组
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
} rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
#if (NGX_THREADS)
rev[i].lock = &c[i].lock;
rev[i].own_lock = &c[i].lock;
#endif
}
//创建一个写事件数组
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
} wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
#if (NGX_THREADS)
wev[i].lock = &c[i].lock;
wev[i].own_lock = &c[i].lock;
#endif
} i = cycle->connection_n;
next = NULL;
//初始化整个connection数组
do {
i--; c[i].data = next;
c[i].read = &cycle->read_events[i];
c[i].write = &cycle->write_events[i];
c[i].fd = (ngx_socket_t) -1; next = &c[i]; #if (NGX_THREADS)
c[i].lock = 0;
#endif
} while (i); cycle->free_connections = next;
cycle->free_connection_n = cycle->connection_n; /* for each listening socket */
//为每一个监听套接字从connection数组中分配一个连接,即一个slot
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
//从conneciton中取得一个新的连接solt
c = ngx_get_connection(ls[i].fd, cycle->log); if (c == NULL) {
return NGX_ERROR;
} c->log = &ls[i].log; c->listening = &ls[i];
ls[i].connection = c; rev = c->read; rev->log = c->log;
rev->accept = 1; //读时间发生,调用accept #if (NGX_HAVE_DEFERRED_ACCEPT)
//省略
#endif if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ls[i].previous) { /*
* delete the old accept events that were bound to
* the old cycle read events array
*/ old = ls[i].previous->connection; if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
} old->fd = (ngx_socket_t) -1;
}
} //注冊监听套接口毒事件的回调函数 ngx_event_accept
rev->handler = ngx_event_accept; //使用了accept_mutex,临时不将监听套接字放入epoll中,而是
//等到worker抢到accept相互排斥体后,再放入epoll,避免惊群的发生
if (ngx_use_accept_mutex) {
continue;
} if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
if (ngx_add_conn(c) == NGX_ERROR) {
return NGX_ERROR;
} } else {
//没有使用accept相互排斥体,那么就将此监听套接字放入epoll中。
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
} #endif } return NGX_OK;
}
nginx源代码分析--event事件驱动初始化的更多相关文章
- 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t
nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...
- SDL2源代码分析1:初始化(SDL_Init())
===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...
- Nginx源代码分析—业务流程
Nginx源代码分析-业务流程 到此为止,我们如果ngx_init_cycle已经结束.我们临时无论他做了什么,我们从他做的效果进入. 从常理上来讲,假设一个请求到达,那么我们须要接受这个请求,那么就 ...
- nginx源代码分析--进程间通信机制 & 同步机制
Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...
- nginx源代码分析之内存池实现原理
建议看本文档时结合nginx源代码. 1.1 什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理.应用程序申请内存时不再与OS打交道.而是从内存池中申请内存或者释放内存到内存池 ...
- nginx源代码分析--事件模块 & 琐碎
通过HUP信息使得NGINX实现又一次读取配置文件,使用USR2信号使得NGINX实现平滑升级. 在nginx中有模块这么一说,对外全部的模块都是ngx_module_t类型,这个结构体作为全部模块的 ...
- nginx源代码分析--从源代码看nginx框架总结
nginx源代码总结: 1)代码中没有特别绕特别别扭的编码实现.从变量的定义调用函数的实现封装,都非常恰当.比方从函数命名或者变量命名就能够看出来定义的大体意义,函数的基本功能,再好的架构实如今编码习 ...
- nginx源代码分析--配置文件解析
ngx-conf-parsing 对 Nginx 配置文件的一些认识: 配置指令具有作用域,分为全局作用域和使用 {} 创建其他作用域. 同一作用域的不同的配置指令没有先后顺序:同一作用域能否使用同样 ...
随机推荐
- 基于visual Studio2013解决面试题之1402选择排序
题目
- <转载>网页设计中的F式布局
地址:http://www.uisdc.com/understanding-the-f-layout-in-web-design 网页设计中的F式布局 今天我们来重点介绍网页设计中的F式布局.传统的布 ...
- java web解决表单重复提交问题
我们大家再进行web开发的时候,必不可少会遇见表单重复提交问题.今天就来给总结如何解决表单提交问题,欢迎大家交流指正. 首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提 ...
- DEBUG模式下, 内存中的变量地址分析
测试函数的模板实现 /// @file my_template.h /// @brief 测试数据类型用的模板实现 #ifndef MY_TEMPLATE_H_2016_0123_1226 #defi ...
- Joomla3.1.1在64位win7下安装
将下载的joomla压缩包解压到Apache下的htdocs文件夹中. 打开Apache服务器,在浏览器输入http://localhost:8081/Joomla/ 即可进入Joolma的安装配置界 ...
- JSP:JAVA Bean在JSP中的运用
目录结构,如图: index.jsp <%@ page language="java" import="java.util.*" pageEncoding ...
- Visual Studio 必备神器---转
会使用工具是人类文明的一大进步,今天敏捷大行其道,好的工具可以大大的提高生产力,这里说的工具都是VS平台上的扩展工具,一些机械的部分可以交给工具去处理,自己多关注其他部分.下面分享下我觉得不错的工具, ...
- Windows phone 8 学习笔记
Windows phone 8 学习笔记(1) 触控输入 http://www.apkbus.com/android-138547-1-1.html Windows phone 8 学习笔记(2) ...
- inode结构体成员详解
概述:inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间.档名.使用者及群组等.inode分为内存中的inode和文件系统中的inode,为了避免混淆,我们称前者为VFS ino ...
- 管道是如何处理HTTP请求的?
管道是如何处理HTTP请求的? 我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很 ...