Nginx中的ngx_modules数组
1.ngx_modules数组的产生
ngx_modules数组是在执行configure脚本后自动生成的,在objs/ngx_modules.c文件中。该数组即当前编译版本中的所有Nginx模块。
做如下操作,可以看到ngx_moduels数组的一般形式:
1.1 最简单的Nginx框架,不包含任何HTTP模块
cd nginx-1.6.2
./configure --without-http
此时,objs/ngx_modules.c的内容为:
#include <ngx_config.h>
#include <ngx_core.h>
extern ngx_module_t ngx_core_module;
extern ngx_module_t ngx_errlog_module;
extern ngx_module_t ngx_conf_module;
extern ngx_module_t ngx_events_module;
extern ngx_module_t ngx_event_core_module;
extern ngx_module_t ngx_epoll_module;
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
NULL
};
1.2 默认的ngx_modules数组:
make clean
cd nginx-1.6.2
./configure
此时,objs/ngx_modules.c的内容为:
#include <ngx_config.h>
#include <ngx_core.h>
extern ngx_module_t ngx_core_module;
extern ngx_module_t ngx_errlog_module;
extern ngx_module_t ngx_conf_module;
extern ngx_module_t ngx_events_module;
extern ngx_module_t ngx_event_core_module;
extern ngx_module_t ngx_epoll_module;
extern ngx_module_t ngx_regex_module;
extern ngx_module_t ngx_http_module;
extern ngx_module_t ngx_http_core_module;
extern ngx_module_t ngx_http_log_module;
extern ngx_module_t ngx_http_upstream_module;
extern ngx_module_t ngx_http_static_module;
extern ngx_module_t ngx_http_autoindex_module;
extern ngx_module_t ngx_http_index_module;
extern ngx_module_t ngx_http_auth_basic_module;
extern ngx_module_t ngx_http_access_module;
extern ngx_module_t ngx_http_limit_conn_module;
extern ngx_module_t ngx_http_limit_req_module;
extern ngx_module_t ngx_http_geo_module;
extern ngx_module_t ngx_http_map_module;
extern ngx_module_t ngx_http_split_clients_module;
extern ngx_module_t ngx_http_referer_module;
extern ngx_module_t ngx_http_rewrite_module;
extern ngx_module_t ngx_http_proxy_module;
extern ngx_module_t ngx_http_fastcgi_module;
extern ngx_module_t ngx_http_uwsgi_module;
extern ngx_module_t ngx_http_scgi_module;
extern ngx_module_t ngx_http_memcached_module;
extern ngx_module_t ngx_http_empty_gif_module;
extern ngx_module_t ngx_http_browser_module;
extern ngx_module_t ngx_http_upstream_ip_hash_module;
extern ngx_module_t ngx_http_upstream_least_conn_module;
extern ngx_module_t ngx_http_upstream_keepalive_module;
extern ngx_module_t ngx_http_write_filter_module;
extern ngx_module_t ngx_http_header_filter_module;
extern ngx_module_t ngx_http_chunked_filter_module;
extern ngx_module_t ngx_http_range_header_filter_module;
extern ngx_module_t ngx_http_gzip_filter_module;
extern ngx_module_t ngx_http_postpone_filter_module;
extern ngx_module_t ngx_http_ssi_filter_module;
extern ngx_module_t ngx_http_charset_filter_module;
extern ngx_module_t ngx_http_userid_filter_module;
extern ngx_module_t ngx_http_headers_filter_module;
extern ngx_module_t ngx_http_copy_filter_module;
extern ngx_module_t ngx_http_range_body_filter_module;
extern ngx_module_t ngx_http_not_modified_filter_module;
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
&ngx_regex_module,
&ngx_http_module,
&ngx_http_core_module,
&ngx_http_log_module,
&ngx_http_upstream_module,
&ngx_http_static_module,
&ngx_http_autoindex_module,
&ngx_http_index_module,
&ngx_http_auth_basic_module,
&ngx_http_access_module,
&ngx_http_limit_conn_module,
&ngx_http_limit_req_module,
&ngx_http_geo_module,
&ngx_http_map_module,
&ngx_http_split_clients_module,
&ngx_http_referer_module,
&ngx_http_rewrite_module,
&ngx_http_proxy_module,
&ngx_http_fastcgi_module,
&ngx_http_uwsgi_module,
&ngx_http_scgi_module,
&ngx_http_memcached_module,
&ngx_http_empty_gif_module,
&ngx_http_browser_module,
&ngx_http_upstream_ip_hash_module,
&ngx_http_upstream_least_conn_module,
&ngx_http_upstream_keepalive_module,
&ngx_http_write_filter_module,
&ngx_http_header_filter_module,
&ngx_http_chunked_filter_module,
&ngx_http_range_header_filter_module,
&ngx_http_gzip_filter_module,
&ngx_http_postpone_filter_module,
&ngx_http_ssi_filter_module,
&ngx_http_charset_filter_module,
&ngx_http_userid_filter_module,
&ngx_http_headers_filter_module,
&ngx_http_copy_filter_module,
&ngx_http_range_body_filter_module,
&ngx_http_not_modified_filter_module,
NULL
};
2.直接使用ngx_modules的场景
如上所述,ngx_modules数组代表了当前Nginx中的所有模块,由于每一个Nginx模块,即ngx_module_t类型的变量,其ctx变量一般为如下几个类型之一:
- ngx_core_module_t
- ngx_event_module_t
- ngx_http_module_t
- ngx_mail_module_t
所以,可以对所有的Nginx模块按其ctx类型进行分类,当然并不是所有的nginx模块都有ctx成员,例如ngx_conf_module模块的ctx成员为NULL,但可以认为绝大部分模块都属于上述四类模块之一。
每一类模块下的所有模块,由于其ctx结构是一样的,因而在程序执行逻辑上会有共同点。具体来说,我们可以遍历ngx_modules数组成员,判断其属于上述四类的哪一类模块,再调用其对应的ctx成员中的函数指针,这样就会屏蔽掉具体的模块名称,抽象到框架的层面上。
那么,如何判断一个模块属于上述四类模块中的哪一类呢?这就是模块变量的type成员的作用了。
- 所有的ngx_core_module_t类型的模块,其type成员为NGX_CORE_MODULE
- 所有的ngx_event_module_t类型的模块,其type成员为NGX_EVENT_MODULE
- 所有的ngx_http_module_t类型的模块,其type成员为NGX_HTTP_MODULE
- 所有的ngx_mail_module_t类型的模块,其type成员为NGX_MAIL_MODULE
所以,在遍历ngx_modules数组时,即可根据每一个数组成员的type成员,来判断该模块属于哪种类型的模块,进而执行该模块对应的钩子函数。
下面,我们以nginx-1.6.2为例,看看程序在哪些地方对nginx_modules数组进行了遍历。
2.1 统计模块数量
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
上述代码在nginx.c的main函数中,可以看到,其作用为:
- 统计系统中到底有多少个模块,记录在全局变量ngx_max_module中。
- 将每个模块在ngx_modules数组中的序号,记录在模块的index变量中。
2.2 用来解析每个模块的配置
具体见函数ngx_conf_handler。
2.3 执行每个模块的ctx中的钩子函数
例如,ngx_init_cycle函数中:
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_core_module_t类型的模块,调用其ctx成员中的create_conf钩子函数。
2.4 同样在在ngx_init_cycle函数:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
if (module->init_conf) {
if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
== NGX_CONF_ERROR)
{
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
以上代码对所有的ngx_core_module_t类型的模块,调用其ctx成员的init_conf钩子函数。
2.5 继续看ngx_init_cycle函数:
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);
}
}
}
以上代码调用所有的nginx模块(ngx_module_t变量)的init_module钩子函数(如果不为空的话)。
2.6 在ngx_event_process_init函数中:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
}
break;
}
调用event模块的ctx成员中的actions.init钩子函数,对于epoll,则是ngx_epoll_init函数。
2.7 在ngx_events_block函数中:
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++;
}
上面的代码,对于所有的NGX_EVENT_MODULE类型的模块,初始化其ctx_index变量。主要有两点:
- ngx_event_max_module代表了ngx_event_module_t类型模块的个数。而ngx_max_module则代表了所有模块的个数。
- ctx_index成员表示该模块在该类模块(本例中为ngx_event_module_t类型的模块)中的序号。
2.8 在ngx_events_block函数中:
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;
}
}
}
上面的代码,对于所有的NGX_EVENT_MODULE类型的模块,调用其ctx成员中的create_conf钩子函数。
2.9 在ngx_events_block函数中:
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;
}
}
}
上面的代码,对于所有的NGX_EVENT_MODULE类型的模块,调用其ctx成员中的init_conf钩子函数。
2.10 在ngx_event_use函数中:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->name->len == value[1].len) {
if (ngx_strcmp(module->name->data, value[1].data) == 0) {
ecf->use = ngx_modules[m]->ctx_index;
ecf->name = module->name->data;
if (ngx_process == NGX_PROCESS_SINGLE
&& old_ecf
&& old_ecf->use != ecf->use)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"when the server runs without a master process "
"the \"%V\" event type must be the same as "
"in previous configuration - \"%s\" "
"and it cannot be changed on the fly, "
"to change it you need to stop server "
"and start it again",
&value[1], old_ecf->name);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
}
}
ngx_event_use是ngx_event_core_module模块的指令"use"的解析函数。
2.11 在函数ngx_http_block中:
ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
ngx_modules[m]->ctx_index = ngx_http_max_module++;
}
上面的代码主要做了两件事情:
- 遍历所有的ngx_http_module_t类型的模块,统计这种模块的个数,存在变量ngx_http_max_module中。
- 初始化每个ngx_http_module_t类型的模块的ctx_index变量,该值的涵义是每个ngx_http_module_t模块在所有该类模块中的序号。
2.12 在函数ngx_http_block中:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
if (ctx->loc_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
上面的代码分别调用所有ngx_http_module_t类型模块的create_main_conf、create_srv_conf和create_loc_conf钩子函数。
2.13 同样在ngx_http_block函数中:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
调用所有ngx_http_module_t类型模块的preconfiguration钩子函数。
2.14 同样在ngx_http_block函数中:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
/* init http{} main_conf's */
if (module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
上面的代码首先调用所有ngx_http_module_t类型模块的init_main_conf钩子函数,然后对main, server, location等配置进行了合并。
2.15 仍然看ngx_http_block函数:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
很明显,上面的代码调用了所有ngx_http_module_t类型的模块的postconfiguration钩子函数。
2.16 在函数ngx_mail_block中:
ngx_mail_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
continue;
}
ngx_modules[m]->ctx_index = ngx_mail_max_module++;
}
不难看出,该段代码主要完成两点功能:
- 统计所有的ngx_mail_module_t类型的模块,存入变量ngx_mail_max_module中。
- 初始化每个ngx_mail_module_t类型模块的ctx_index变量,该变量表示每个该类变量在所有ngx_mail_module_t变量中的序号。
mail与http类似,还有许多关于ngx_mail_module_t的ctx变量的钩子函数的调用,这里略过。
2.17 在函数ngx_single_process_cycle中:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_process) {
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
/* fatal */
exit(2);
}
}
}
以上代码调用所有模块的init_process钩子函数。
2.18 在函数ngx_master_process_exit中:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->exit_master) {
ngx_modules[i]->exit_master(cycle);
}
}
以上代码调用所有模块的exit_process钩子函数。
2.19 在函数ngx_worker_process_init函数中:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_process) {
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
/* fatal */
exit(2);
}
}
}
上面的代码调用所有模块的init_process钩子函数.
2.20 在函数ngx_worker_process_exit中:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->exit_process) {
ngx_modules[i]->exit_process(cycle);
}
}
上面的代码调用了所有模块的exit_process钩子函数。