一、配置文件處理的主要步驟和過程
nginx.c文件main函數
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
初始化所有模塊的索引順序ngx_modules[i]->index,和ngx_max_module。
調用ngx_init_cycle(&init_cycle)。
ngx_init_cycle
1.創建conf_ctx存放所有配置結構體
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
2.NGX_CORE_MODULE模塊配置結構體初始化,並存放於cycle->conf_ctx數組中。
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;
}
}
3.調用ngx_conf_param()和ngx_conf_parse()解析配置文件。具體看下一個章節。
conf.ctx = cycle->conf_ctx;
if (ngx_conf_param(&conf) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
4.調用NGX_CORE_MODULE模塊的init_conf函數。
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;
}
}
}
5.調用module的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);
}
}
}
ngx_conf_parse()函數
截取主要代碼部分。讀取一個配置指令,交給ngx_conf_handler進行處理,如果返回NGX_ERROR則出錯結束。
for ( ;; ) {
rc = ngx_conf_read_token(cf);
rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) {
goto failed;
}
}
ngx_conf_handler函數
1. 遍歷所有模塊的cmd數組,比較token是否和cmd的指令名稱相同。當找到一個相同的之後,進入下一步。
先看以下ngx_command_s結構體的定義。
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;
};
指令參數個數:NGX_CONF_NOARGS(不接收參數),NGX_CONF_TAKE1(一個參數),NGX_CONF_TAKE12(一個或兩個參數)。
指令值類型:NGX_CONF_BLOCK(配置信息塊),NGX_CONF_FLAG(接收“on"或"off")
指令出現位置:NGX_DIRECT_CONF(配置文件中最外層,如daemon,master_process等),NGX_MAIN_CONF(http, mail, events, error_log等),NGX_HTTP_MAIN_CONF(http配置指令裏),NGX_HTTP_SRV_CONF(http配置指令裏的server配置指令裏),NGX_HTTP_LOC_CONF(http配置指令裏的server配置指令裏的location配置指令裏),還有其他的。
set:是在解析配置文件時,遇到這個指令時被調用的函數。
conf:指定配置信息存儲的內存位置。NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET, NGX_HTTP_LOC_CONF_OFFSET。(這個值在獲取指令對應的conf時會被用到)。
offset:定義指令將配置的結構體的哪個成員。(後面說明如何被調用)
2. 檢查當前指令是否出現在合法的位置。配置文件是嵌套解析的,當進入http的部分時,cf->cmd_type會被設置爲NGX_HTTP_MAIN_CONF。所以只有那些cmd->type包含這個值的纔會被處理,否則會被跳過。
if (!(cmd->type & cf->cmd_type)) {
continue;
}
3.檢查指令參數個數是否合理。
4.獲取指令對應的conf(配置結構體,用於存放指令的設置值)。
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[ngx_modules[i]->index];
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[ngx_modules[i]->ctx_index];
}
}
對於HTTP模塊,會執行第三種情況。這時cf->ctx對應的是http的配置結構體ngx_http_conf_ctx_t,包含三個數組main_conf, srv_conf, loc_conf。使用cmd->conf等到對應的數組。再通過ngx_modules[i]->ctx_index獲得模塊對應的conf結構體(在http指令被解析到的時候,已經通過調用模塊的create_main_conf, create_srv_conf, create_loc_conf創建,並存儲在對應的數組中)。
5.調用指令定義時留下的set函數。
rv = cmd->set(cf, cmd, conf);
NGX_HTTP_MODULE: ngx_http.c文件
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.創建三個數組main_conf, srv_conf, loc_conf用於存放所有HTTP模塊將產生的配置結構體指針。調用每個模塊的create_main_conf, create_srv_conf, create_loc_conf,並將返回的配置結構體指針放在對應的數組當中。
ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
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;
}
}
}
3. 調用每個模塊的preconfiguration函數進行預處理。
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;
}
}
}
4. 進入http配置指令的內部指令解析過程,包括server配置指令結構,location配置指令結構等。
pcf = *cf; /* pcf保存原配置信息 */
cf->ctx = ctx;
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF; /* 改變配置文件解析位置信息 */
rv = ngx_conf_parse(cf, NULL);
*cf = pcf; /* 最後在返回前,恢復 */
5. init http{} main_conf's, merge the server{}s' srv_conf's and its location{}s' loc_conf's 和create location trees。這部分還沒看懂。
6.init_phases
7.調用HTTP模塊的postconfiguration函數,做後期處理。例如,默認值處理可以放在這裏執行。
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;
}
}
}
8. ngx_http_variables_init_vars
9.ngx_http_init_phase_handlers
10.ngx_http_optimize_servers(),調用ngx_http_init_listening -> 調用ngx_http_add_listening:設置ls->handler = ngx_http_init_connection,其中ls爲ngx_listening_t結構體。
NGX_EVENT_MODULE: ngx_events.c文件
當遇到'events'指令時,將調用ngx_events_block()函數。執行以下內容:
1. 計算NGX_EVENT_MODULE的數量
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++;
}
2.創建指針數組存放NGX_EVENT_MODULE模塊的conf結構體。
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;
3. 調用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;
}
}
}
4.進入events配置模塊,進行解析。
pcf = *cf;
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
5.調用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;
}
}
}
二、問題解析
ngx_command_s結構體中offset如何被使用?
char *
ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_str_t *field, *value;
ngx_conf_post_t *post;
field = (ngx_str_t *) (p + cmd->offset); /* 使用offset獲取結構體成員地址指針 */
if (field->data) {
return "is duplicate";
}
value = cf->args->elts;
*field = value[1];
if (cmd->post) {
post = cmd->post;
return post->post_handler(cf, post, field);
}
return NGX_CONF_OK;
}