基础知识一——模块
先看一下nginx 的模块设计,官方nginx共有五大类型的模块:核心模块、配置模块、事件模块、HTTP模块、MAIL模块。
各个模块的实现如下图:
ngx_module_t结构体作为所有模块的通用接口。ngx_command_t类型的commands数组则指定了模块处理配置项的方法。ngx_module_t中的ctx成员是一个void*指针,它可以指向任何数据,这给模块提供了很大的灵活性,使得多层次、多类型的模块设计成为可能。
核心模块包括:ngx_core_module、ngx_mail_module、ngx_http_module、ngx_openssl_module、ngx_events_module、ngx_errlog_module。
它们的ctx上下文为:
typedef struct {
//核心模块名称
ngx_str_t name;
//解析配置项前,Nginx框架会调用create_conf方法。
void *(*create_conf)(ngx_cycle_t *cycle);
//解析配置项完成后,nginx框架会调用init_conf方法
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
http模块的ctx上下文:
typedef struct {
//在解析http{...}内的配置项前回调
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
//在解析http{...}内的配置项后回调
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
//创建用于存储HTTP全局配置项的结构体,该结构体的成员将保存直属于http{}块的配置项参数。它会在解析main配置项后回调。
void *(*create_main_conf)(ngx_conf_t *cf);
//解析完main配置项后回调
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
//创建用于存储可同时出现在mian、srv级别配置项的结构体,该结构体中的成员与server配置是相关联的。
void *(*create_srv_conf)(ngx_conf_t *cf);
//把出现在main级别中的配置项合并到srv级别配置项中。
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
//创建用于存储可同时出现在main、srv、loc级别配置项的结构体,该结构体中的成员与location配置是相关联的。
void *(*create_loc_conf)(ngx_conf_t *cf);
//把出现在main、srv级别的配置项值合并到loc级别的配置项中。
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
基础知识二——ngx_module[]数组
nginx是通过全局指针数组ngx_modules[]组织模块的,数组的每一个元素均为一个全局ngx_module_t对象的指针。
00052: ngx_module_t *ngx_modules[] = {
00053: &ngx_core_module,
00054: &ngx_errlog_module,
00055: &ngx_conf_module,
00056: &ngx_events_module,
00057: &ngx_event_core_module,
00058: &ngx_epoll_module,
00059: &ngx_http_module,
00060: &ngx_http_core_module,
00061: &ngx_http_log_module,
00062: &ngx_http_upstream_module,
00063: &ngx_http_static_module,
00064: &ngx_http_autoindex_module,
00065: &ngx_http_index_module,
00066: &ngx_http_auth_basic_module,
00067: &ngx_http_access_module,
00068: &ngx_http_limit_zone_module,
00069: &ngx_http_limit_req_module,
00070: &ngx_http_geo_module,
00071: &ngx_http_map_module,
00072: &ngx_http_split_clients_module,
00073: &ngx_http_referer_module,
00074: &ngx_http_rewrite_module,
00075: &ngx_http_proxy_module,
00076: &ngx_http_fastcgi_module,
00077: &ngx_http_uwsgi_module,
00078: &ngx_http_scgi_module,
00079: &ngx_http_memcached_module,
00080: &ngx_http_empty_gif_module,
00081: &ngx_http_browser_module,
00082: &ngx_http_upstream_ip_hash_module,
00083: &ngx_http_stub_status_module,
00084: &ngx_http_write_filter_module,
00085: &ngx_http_header_filter_module,
00086: &ngx_http_chunked_filter_module,
00087: &ngx_http_range_header_filter_module,
00088: &ngx_http_gzip_filter_module,
00089: &ngx_http_postpone_filter_module,
00090: &ngx_http_ssi_filter_module,
00091: &ngx_http_charset_filter_module,
00092: &ngx_http_userid_filter_module,
00093: &ngx_http_headers_filter_module,
00094: &ngx_http_copy_filter_module,
00095: &ngx_http_range_body_filter_module,
00096: &ngx_http_not_modified_filter_module,
00097: NULL
00098: };
00099:
nginx启动总流程
core module create_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
//得到core module
module = ngx_modules[i]->ctx;
//如果create_conf存在,则直接创建config.
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
//保存config.
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
此时的内存布局:
解析配置文件
当所有的core module的config都创建完毕后,就要开始解析配置文件了,解析配置文件它会一行行读取,然后如果遇到指令,则会查找到对应的ngx_command_t对象,然后执行对应的回调set方法。这里所有动作都在ngx_conf_parse这个函数中进行。
解析HTTP
当解析到http{}时,就回调ngx_http_block方法,这个方法包括了HTTP框架的完整初始化流程。
1)步源码:
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++;
}
2)-5)步源码:
ngx_http_conf_ctx_t *ctx;
//开始初始化,可以看到默认会分配max个config
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
}
//下面省略了srv和loc的创建
.......................................
//开始遍历
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
//得到对应的module上下文
module = ngx_modules[m]->ctx;
//得到对应的索引
mi = ngx_modules[m]->ctx_index;
//如果有对应的回调,则调用回调函数,然后将返回的模块config设置到ctx的对应的conf列表中。
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;
}
}
//下面省略了loc的调用。
}
相关结构:
typedef struct {
/*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_main_conf方法创建的存放全局配置项的结构体,他们存放着解析直属http{}块内的main级别的配置项参数。*/
void **main_conf;
/*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_srv_conf方法创建的与server相关的结构体,他们或存放main级别配置项,或存放srv级别配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}或者server{}块时创建的有关。*/
void **srv_conf;
/*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_loc_conf方法创建的与location相关的结构体,他们可能存放main、srv、loc级别的配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}、server{}或location{}快时创建的有关。*/
void **loc_conf;
} ngx_http_conf_ctx_t;
6)步源码:
cf->ctx = ctx;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
//调用preconfiguration。
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
此时内存布局:
7)步源码:
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);
这一步会解析main级别文件:内存分析晚些整理上来。
8)-9)步源码:
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 */
//如果有init_main_conf,则首先初始化main conf.
if (module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
//然后开始merge config。
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if (rv != NGX_CONF_OK) {
goto failed;
}
}