nginx源码分析之模块初始化
在nginx啟動(dòng)過程中,模塊的初始化是整個(gè)啟動(dòng)過程中的重要部分,而且了解了模塊初始化的過程對應(yīng)后面具體分析各個(gè)模塊會(huì)有事半功倍的效果。在我看來,分析源碼來了解模塊的初始化是最直接不過的了,所以下面主要通過結(jié)合源碼來分析模塊的初始化過程。
稍微了解nginx的人都知道nginx是高度模塊化的,各個(gè)功能都封裝在模塊中,而各個(gè)模塊的初始化則是根據(jù)配置文件來進(jìn)行的,下面我們會(huì)看到nginx邊解析配置文件中的指令,邊初始化指令所屬的模塊,指令其實(shí)就是指示怎樣初始化模塊的。
模塊初始化框架
模塊的初始化主要在函數(shù)ngx_init_cycle(src/ngx_cycle.c)中下列代碼完成:
1 ngx_cycle_t * 2 ngx_init_cycle(ngx_cycle_t *old_cycle) 3 { 4 ... 5 //配置上下文 6 cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); 7 ... 8 //處理core模塊,cycle->conf_ctx用于存放所有CORE模塊的配置 9 for (i = 0; ngx_modules[i]; i++) { 10 if (ngx_modules[i]->type != NGX_CORE_MODULE) { //跳過不是nginx的內(nèi)核模塊 11 continue; 12 } 13 module = ngx_modules[i]->ctx; 14 //只有ngx_core_module有create_conf回調(diào)函數(shù),這個(gè)會(huì)調(diào)用函數(shù)會(huì)創(chuàng)建ngx_core_conf_t結(jié)構(gòu), 15 //用于存儲整個(gè)配置文件main scope范圍內(nèi)的信息,比如worker_processes,worker_cpu_affinity等 16 if (module->create_conf) { 17 rv = module->create_conf(cycle); 18 ... 19 cycle->conf_ctx[ngx_modules[i]->index] = rv; 20 } 21 } 22 //conf表示當(dāng)前解析到的配置命令上下文,包括命令,命令參數(shù)等 23 conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); 24 ... 25 conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); 26 ... 27 conf.ctx = cycle->conf_ctx; 28 conf.cycle = cycle; 29 conf.pool = pool; 30 conf.log = log; 31 conf.module_type = NGX_CORE_MODULE; //conf.module_type指示將要解析這個(gè)類型模塊的指令 32 conf.cmd_type = NGX_MAIN_CONF; //conf.cmd_type指示將要解析的指令的類型 33 //真正開始解析配置文件中的每個(gè)命令 34 if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { 35 ... 36 } 37 ... 38 //初始化所有core module模塊的config結(jié)構(gòu)。調(diào)用ngx_core_module_t的init_conf, 39 //在所有core module中,只有ngx_core_module有init_conf回調(diào), 40 //用于對ngx_core_conf_t中沒有配置的字段設(shè)置默認(rèn)值 41 for (i = 0; ngx_modules[i]; i++) { 42 if (ngx_modules[i]->type != NGX_CORE_MODULE) { 43 continue; 44 } 45 module = ngx_modules[i]->ctx; 46 if (module->init_conf) { 47 if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index]) 48 == NGX_CONF_ERROR) 49 { 50 environ = senv; 51 ngx_destroy_cycle_pools(&conf); 52 return NULL; 53 } 54 } 55 } 56 ... 57 }? cycle->conf_ctx是一個(gè)指針數(shù)組,數(shù)組中的每個(gè)元素對應(yīng)某個(gè)模塊的配置信息。ngx_conf_parse用戶真正解析配置文件中的命令,conf存放解析配置文件的上下文信息,如module_type表示將要解析模塊的類型,cmd_type表示將要解析的指令的類型,ctx指向解析出來信息的存放地址,args存放解析到的指令和參數(shù)。具體每個(gè)模塊配信息的存放如下圖所示,NGX_MAIN_CONF表示的是全局作用域?qū)?yīng)的配置信息,NGX_EVENT_CONF表示的是EVENT模塊對應(yīng)的配置信息,NGX_HTTP_MAIN_CONF,NGX_HTTP_SRV_CONF,NGX_HTTP_LOC_CONF表示的是HTTP模塊對應(yīng)的main,server,local域的配置信息。
? 下面我們來具體看下ngx_conf_parse函數(shù)是怎樣解析每個(gè)指令的。
1 char * 2 ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) 3 { 4 ... 5 if (filename) { 6 //打開配置文件 7 fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 8 ... 9 prev = cf->conf_file; 10 cf->conf_file = &conf_file; 11 //獲取配置文件信息 12 if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) { 13 ... 14 } 15 //配置緩沖區(qū)用于存放配置文件信息 16 cf->conf_file->buffer = &buf; 17 //NGX_CONF_BUFFER = 4096,直接malloc 18 buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); 19 ... 20 //[pos,last)表示緩存中真正存儲了數(shù)據(jù)的區(qū)間,[start,end)表示緩存區(qū)的物理區(qū)域 21 buf.pos = buf.start; 22 buf.last = buf.start; 23 buf.end = buf.last + NGX_CONF_BUFFER; 24 buf.temporary = 1; 25 cf->conf_file->file.fd = fd; 26 cf->conf_file->file.name.len = filename->len; 27 cf->conf_file->file.name.data = filename->data; 28 cf->conf_file->file.offset = 0; 29 cf->conf_file->file.log = cf->log; 30 cf->conf_file->line = 1; 31 type = parse_file; 32 33 } 34 ... 35 for ( ;; ) { 36 rc = ngx_conf_read_token(cf); //從配置文件中讀取下一個(gè)命令 37 ... 38 rc = ngx_conf_handler(cf, rc); //查找命令所在的模塊,執(zhí)行命令對應(yīng)的函數(shù) 39 ... 40 } 41 ... 42 }ngx_conf_parse是配置解析的入口函數(shù),它根據(jù)當(dāng)成上下文讀取配置文件數(shù)據(jù),進(jìn)行分析的同時(shí)對配置文件語法進(jìn)行檢查。同時(shí),如果遇到的指令包含塊,這個(gè)函數(shù)會(huì)在指令處理函數(shù)修改作用域等上下文信息后,被間接遞歸調(diào)用來處理塊中的配置信息。第一次調(diào)用ngx_conf_parse時(shí),函數(shù)會(huì)打開配置文件,設(shè)置正在執(zhí)行的解析類型,讀取命令,然后調(diào)用ngx_conf_handler。我們先來看下ngx_conf_read_token怎么讀取命令的:
1 //解析一個(gè)命令和其所帶的參數(shù) 2 static ngx_int_t 3 ngx_conf_read_token(ngx_conf_t *cf) 4 { 5 ... 6 found = 0; //標(biāo)記是否找到一個(gè)命令 7 need_space = 0; //下一個(gè)字符希望是空格 8 last_space = 1; //上一個(gè)字符是空格 9 sharp_comment = 0; //注釋 10 variable = 0; //存在變量 11 quoted = 0; //\符號 12 s_quoted = 0; //單引號 13 d_quoted = 0; //雙引號 14 ... 15 for ( ;; ) { 16 ch = *b->pos++; //當(dāng)前字符存儲于ch中 17 if (ch == LF) { //換行 18 if (sharp_comment) { //注釋結(jié)束,只有行注釋 19 sharp_comment = 0; 20 } 21 } 22 if (sharp_comment) { //跳過注釋,直到換行 23 continue; 24 } 25 if (quoted) { //如果上一個(gè)字符是\,則跳過當(dāng)前字符,后面讀取token會(huì)做處理 26 quoted = 0; 27 continue; 28 } 29 if (need_space) { //次字符必須是space分割符 30 /* 31 忽略掉space字符;space字符包括' ','\t',CR,LF 32 如果字符是';','{',此次命令讀取結(jié)束; 33 如果字符是')',開始讀取新的token; 34 如果讀到其它字符則解析失敗 35 */ 36 } 37 if (last_space) { //表示一個(gè)token開始了,第一個(gè)非space字符 38 /* 39 忽略掉space字符; 40 如果字符是';','{','}',此次命令讀取結(jié)束; 41 如果字符是'#',字符后面是注釋; 42 如果字符是'\',下一個(gè)字符將被忽略 43 如果字符是'"',''',下一個(gè)字符在單引號或雙引號中 44 */ 45 } else { //開始讀取新token 46 /* 47 如果字符是'$',后面的非space字符組成變量token; 48 碰到了結(jié)束引號'"','''時(shí),token讀取完成; 49 碰到了space字符,token讀取完成 50 */ 51 if (found){ 52 /* 53 將找到的token追加到cf->args數(shù)組中,并且每個(gè)token字符串以'\0'結(jié)束 54 */ 55 } 56 } 57 } 58 }? 下面我們再來看下命令處理函數(shù)ngx_conf_handler:
1 static ngx_int_t 2 ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) 3 { 4 ... 5 for (i = 0; ngx_modules[i]; i++) { 6 cmd = ngx_modules[i]->commands; 7 ... 8 for ( /* void */ ; cmd->name.len; cmd++) { 9 //命令名稱對比 10 //模塊類型對比 11 //命令類型對比 12 //命令是否帶塊 13 //檢測命令參數(shù) 14 conf = NULL; 15 if (cmd->type & NGX_DIRECT_CONF) { 16 conf = ((void **) cf->ctx)[ngx_modules[i]->index]; 17 18 } else if (cmd->type & NGX_MAIN_CONF) { 19 conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); 20 21 } else if (cf->ctx) { 22 confp = *(void **) ((char *) cf->ctx + cmd->conf); 23 24 if (confp) { 25 conf = confp[ngx_modules[i]->ctx_index]; 26 } 27 } 28 //調(diào)用命令中的set函數(shù) 29 rv = cmd->set(cf, cmd, conf); 30 ... 31 } 32 } 33 ... 34 }完全讀取到一條配置指令后,Nginx會(huì)使用該指令名在所有指令定義中進(jìn)行查找。但是,在進(jìn)行查找之前,Nginx會(huì)先驗(yàn)證模塊的類型和當(dāng)前解析函數(shù)上下文中的類型是否一致。隨后,在某個(gè)模塊中找到匹配的指令定義后,還會(huì)驗(yàn)證指令可以出現(xiàn)的作用域是否包含當(dāng)前解析函數(shù)上下文中記錄的作用域。最后,檢查指令的參數(shù)個(gè)數(shù)是否和指令定義中標(biāo)明的一致。
校驗(yàn)工作完成后,Nginx將指令名和所有模塊預(yù)定義支持的指令進(jìn)行對比,找到完全匹配的配置指令定義。根據(jù)配置指令的不同類型,配置項(xiàng)的存儲位置也不同。
NGX_DIRECT_CONF類型的配置指令,其配置項(xiàng)存儲空間是全局作用域?qū)?yīng)的存儲空間。這個(gè)類型的指令主要出現(xiàn)在ngx_core_module模塊里。
1 conf = ((void **) cf->ctx)[ngx_modules[i]->index];NGX_MAIN_CONF表示配置指令的作用域?yàn)槿肿饔糜颉?v觀Nginx整個(gè)代碼,除了ngx_core_module的配置指令(同時(shí)標(biāo)識為NGX_DIRECT_CONF)位于這個(gè)作用域中外,另外幾個(gè)定義新的子級作用域的指令–events、http、mail、imap,都是非NGX_DIRECT_CONF的NGX_MAIN_CONF指令,它們在全局作用域中并未被分配空間,所以在指令處理函數(shù)中分配的空間需要掛接到全局作用域中,故傳遞給指令處理函數(shù)的參數(shù)是全局作用域的地址。
1 conf = &(((void **) cf->ctx)[ngx_modules[i]->index];其它類型配置指令項(xiàng)的存儲位置和指令出現(xiàn)的作用域(并且非全局作用域)有關(guān):
1 confp = *(void **) ((char *) cf->ctx + cmd->conf); 2 if (confp) { conf = confp[ngx_modules[i]->ctx_index]; }配置項(xiàng)將要存儲的位置確定后,調(diào)用指令處理函數(shù),完成配置項(xiàng)初始化和其它工作。
rc = cmd->set(cf, cmd, conf);? 下面通過分析模塊HTTP的初始化來進(jìn)一步加深理解模塊的初始化。
HTTP模塊的初始化
http模塊由很多個(gè)模塊組成,這里主要講解兩個(gè)主要模塊的初始化,它們是ngx_http_module和ngx_http_core_module。ngx_http_moduel模塊很簡單,它的定義如下:
1 static ngx_command_t ngx_http_commands[] = { 2 { ngx_string("http"), //http命令描述結(jié)構(gòu) 3 NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, 4 ngx_http_block, 5 0, 6 0, 7 NULL }, 8 ngx_null_command 9 }; 10 static ngx_core_module_t ngx_http_module_ctx = { 11 ngx_string("http"), 12 NULL, 13 NULL 14 }; 15 ngx_module_t ngx_http_module = { 16 NGX_MODULE_V1, 17 &ngx_http_module_ctx, /* module context */ 18 ngx_http_commands, /* module directives */ 19 NGX_CORE_MODULE, /* module type */ 20 ... 21 };從模塊的定義看,它是一個(gè)NGX_CORE_MODULE類型的模塊,就包含一條指令http,當(dāng)在全局作用域中遇到http指令后,會(huì)調(diào)用ngx_http_block函數(shù),傳遞給該函數(shù)的參數(shù)為:cf,當(dāng)前配置上下文信息;cmd,http命令描述結(jié)構(gòu);conf,ngx_http_module模塊在全局作用域數(shù)組中的地址。下面看下ngx_http_block函數(shù):
1 static char * 2 ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3 { 4 ... 5 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); //http main配置結(jié)構(gòu) 6 ... 7 *(ngx_http_conf_ctx_t **) conf = ctx; //將http main配置結(jié)構(gòu)掛到全局作用域上 8 //計(jì)算NGX_HTTP_MODULE類型模塊的個(gè)數(shù),并計(jì)算每個(gè)NGX_HTTP_MODULE模塊在全部NGX_HTTP_MODULE模塊中的下標(biāo) 9 ngx_http_max_module = 0; 10 for (m = 0; ngx_modules[m]; m++) { 11 if (ngx_modules[m]->type != NGX_HTTP_MODULE) { 12 continue; 13 } 14 ngx_modules[m]->ctx_index = ngx_http_max_module++; 15 } 16 //http的main域配置 17 ctx->main_conf = ngx_pcalloc(cf->pool, 18 sizeof(void *) * ngx_http_max_module); 19 ... 20 //http的server域,用來合并server塊中的域 21 ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); 22 ... 23 //http的local域,用來合并server塊中的local塊中的域 24 ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); 25 //創(chuàng)建各個(gè)作用域?qū)?yīng)的配置結(jié)構(gòu) 26 for (m = 0; ngx_modules[m]; m++) { 27 if (ngx_modules[m]->type != NGX_HTTP_MODULE) { 28 continue; 29 } 30 module = ngx_modules[m]->ctx; 31 mi = ngx_modules[m]->ctx_index; 32 if (module->create_main_conf) { 33 ctx->main_conf[mi] = module->create_main_conf(cf); 34 ... 35 } 36 if (module->create_srv_conf) { 37 ctx->srv_conf[mi] = module->create_srv_conf(cf); 38 ... 39 } 40 if (module->create_loc_conf) { 41 ctx->loc_conf[mi] = module->create_loc_conf(cf); 42 ... 43 } 44 } 45 pcf = *cf; 46 cf->ctx = ctx; //更新配置上下文為http的上下文 47 ... 48 //遞歸ngx_conf_parse來調(diào)用處理http包含的塊的配置信息 49 cf->module_type = NGX_HTTP_MODULE; //模塊類型為NGX_HTTP_MODULE 50 cf->cmd_type = NGX_HTTP_MAIN_CONF; //指令的作用域 51 rv = ngx_conf_parse(cf, NULL); 52 ... 53 *cf = pcf; //恢復(fù)配置上下文 54 ... 55 }? 到這里,已經(jīng)創(chuàng)建的配置信息如下圖所示:
? 遞歸調(diào)用ngx_conf_parse后,接下來處理的每個(gè)指令都是NGX_HTTP_MAIN_CONF作用域的,每個(gè)指令的處理函數(shù)會(huì)初始化應(yīng)配置結(jié)構(gòu)信息,而具體的配置信息存放在哪個(gè)數(shù)組里面由指令定義中的conf字段決定,即一定是main_conf,srv_conf和loc_conf之中的一種。NGX_HTTP_MAIN_CONF作用域中的很多指令都屬于ngx_http_core_module模塊,下面看下這個(gè)模塊的定義:
1 static ngx_command_t ngx_http_core_commands[] = { 2 ... 3 { ngx_string("server"), //server指令 4 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, 5 ngx_http_core_server, 6 0, 7 0, 8 NULL }, 9 { ngx_string("location"), //location指令 10 NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, 11 ngx_http_core_location, 12 NGX_HTTP_SRV_CONF_OFFSET, 13 0, 14 NULL }, 15 ... 16 } 17 18 static ngx_http_module_t ngx_http_core_module_ctx = { 19 ngx_http_core_preconfiguration, /* preconfiguration */ 20 NULL, /* postconfiguration */ 21 ngx_http_core_create_main_conf, /* create main configuration */ 22 ngx_http_core_init_main_conf, /* init main configuration */ 23 ngx_http_core_create_srv_conf, /* create server configuration */ 24 ngx_http_core_merge_srv_conf, /* merge server configuration */ 25 ngx_http_core_create_loc_conf, /* create location configuration */ 26 ngx_http_core_merge_loc_conf /* merge location configuration */ 27 }; 28 ngx_module_t ngx_http_core_module = { 29 NGX_MODULE_V1, 30 &ngx_http_core_module_ctx, /* module context */ 31 ngx_http_core_commands, /* module directives */ 32 NGX_HTTP_MODULE, /* module type */ 33 ... 34 };這個(gè)模塊定義了很多指令,其中server和location是比較關(guān)鍵的兩條指令,它們分別處理server和location指令后面所帶有的塊。處理這些塊的方法和處理http指令后面的塊的方法類似,都是修改配置上下文,調(diào)用ngx_conf_parse來處理,當(dāng)然在處理server塊時(shí)只會(huì)生成srv_conf和loc_conf數(shù)組,所有的server塊共享一個(gè)main_conf數(shù)組,在處理location塊時(shí)只會(huì)生成loc_conf數(shù)組,同一個(gè)server塊中的location共享這個(gè)server塊中的ser_conf數(shù)組。
有些指令能同時(shí)出現(xiàn)在http塊,server塊和location塊中,并且底層塊中指令會(huì)覆蓋上層塊中的定義,如果底層塊中指令沒定義,上層塊的指令定義會(huì)傳遞到底層塊中。所以在函數(shù)ngx_http_block調(diào)用ngx_conf_parse解析處理所有的http模塊的配置指令后,調(diào)用ngx_http_merge_servers將http塊中的srv_conf信息傳遞到各個(gè)server塊中,在ngx_http_merge_servers中將server塊中的loc_conf信息合并到所有的locations塊中。
轉(zhuǎn)載于:https://www.cnblogs.com/chengxuyuancc/p/3792258.html
總結(jié)
以上是生活随笔為你收集整理的nginx源码分析之模块初始化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: r语言 线性回归 相关系数_基于R语言的
- 下一篇: ssh连接远程主机执行脚本的环境变量问题