多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > Nginx 模塊自主開(kāi)發(fā)六:源碼剖析配置文件解析過(guò)程

Nginx 模塊自主開(kāi)發(fā)六:源碼剖析配置文件解析過(guò)程

來(lái)源:程序員人生   發(fā)布時(shí)間:2016-07-19 13:17:47 閱讀次數(shù):3146次

 Nginx源碼實(shí)現(xiàn)有1個(gè)很好的優(yōu)點(diǎn)就是模塊化,有點(diǎn)像面向?qū)ο笳Z(yǔ)言的設(shè)計(jì)模式,實(shí)現(xiàn)“高內(nèi)聚,松耦合”,這篇博客主要講授模塊的共有流程
集中在ngx_cycle.c、 ngx_process.c、 ngx_process_cycle.c 和 ngx_event.c代碼中。

共有流程開(kāi)始于解析 nginx 配置,這個(gè)進(jìn)程集中在 ngx_init_cycle 函數(shù)中。 ngx_init_cycle 是 nginx 的1個(gè)核心函數(shù),共有流程中與配置相干的幾個(gè)進(jìn)程都在這個(gè)函數(shù)中實(shí)現(xiàn),其中包括解析 nginx 配置、初始化 CORE模塊,接著是初始化文件句柄,初始化毛病日志,初始化同享內(nèi)存,然后是監(jiān)聽(tīng)端口。可以說(shuō)共有流程 80% 都是現(xiàn)在 ngx_init_cycle 函數(shù)中,其中流程可以參考 3:深入理解Nginx的模塊化 (結(jié)合源碼詳解)

ngx_cycle_t結(jié)構(gòu)體解析

Nginx框架都是圍繞著ngx_cycle_t結(jié)構(gòu)體控制進(jìn)程運(yùn)行的,ngx_cycle_t結(jié)構(gòu)體初始化就是在ngx_init_cycle函數(shù)中。

truct ngx_cycle_s { /* 保存著所有模塊存儲(chǔ)配置項(xiàng)的結(jié)構(gòu)體指針, 它首先是1個(gè)數(shù)組,數(shù)組大小為ngx_max_module,正好與Nginx的module個(gè)數(shù)1樣; 每一個(gè)數(shù)組成員又是1個(gè)指針,指向另外一個(gè)存儲(chǔ)著指針的數(shù)組,因此會(huì)看到void **** 每一個(gè)進(jìn)程中都有1個(gè)唯1的ngx_cycle_t核心結(jié)構(gòu)體,它有1個(gè)成員conf_ctx保護(hù)著所有模塊的配置結(jié)構(gòu)體, 其類(lèi)型是void ****conf_ctx。conf_ctx意義為首先指向1個(gè)成員皆為指針的數(shù)組,其中每一個(gè)成員指針又指向另外1個(gè) 成員皆為指針的數(shù)組,第2個(gè)子數(shù)組中的成員指針才會(huì)指向各模塊生成的配置結(jié)構(gòu)體。這正是為了事件模 塊、http模塊、mail模塊而設(shè)計(jì)的,這有益于不同于NGX_CORE_MODULE類(lèi)型的 特定模塊解析配置項(xiàng)。但是,NGX_CORE_MODULE類(lèi)型的核心模塊解析配置項(xiàng)時(shí),配置項(xiàng)1定是全局的, 不會(huì)從屬于任何{}配置塊的,它不需要上述這類(lèi)雙數(shù)組設(shè)計(jì)。解析標(biāo)識(shí)為NGX_DIRECT_CONF類(lèi)型的配 置項(xiàng)時(shí),會(huì)把void****類(lèi)型的conf_ctx強(qiáng)迫轉(zhuǎn)換為void**,也就是說(shuō),此時(shí),在conf_ctx指向的指針數(shù)組 中,每一個(gè)成員指針不再指向其他數(shù)組,直接指向核心模塊生成的配置鮚構(gòu)體。因此,NGX_DIRECT_CONF 僅由NGX_CORE_MODULE類(lèi)型的核心模塊使用,而且配置項(xiàng)只應(yīng)當(dāng)出現(xiàn)在全局配置中。 */ //初始化見(jiàn)ngx_init_cycle,所有為http{} server{} location{}分配的空間都由該指針指向新開(kāi)辟的空間 //NGX_CORE_MODULE類(lèi)型模塊賦值在ngx_init_cycle //http{}ngx_http_module相干模塊賦值地方在ngx_http_block */ /* 在核心結(jié)構(gòu)體ngx_cycle_t的conf_ctx成員指向的指針數(shù)組中,第7個(gè)指針由ngx_http_module模塊使用(ngx_http_module模塊的index序號(hào)為6, 由于由0開(kāi)始,所以它在ngx_modules數(shù)組中排行第7。在寄存全局配置結(jié)構(gòu)體的conf_ctx數(shù)組中,第7個(gè)成員指向ngx_http_module模塊),這個(gè)指針 設(shè)置為指向解析http{}塊時(shí)生成的ngx_http_conf_ctx_t結(jié)構(gòu)體,而ngx_http_conf_ctx_t的3個(gè)成員則分別指向新分配的3個(gè)指針數(shù)組。新的指針數(shù)組中 成員的意義由每一個(gè)HTTP模塊的ctx_index序號(hào)指定(ctx_index在HTTP模塊中表明它處于HTTP模塊間的序號(hào)),例如,第6個(gè)HTTP模塊的ctx_index是5 (ctx_index一樣由0開(kāi)始計(jì)數(shù)),那末在ngx_http_conf_ctx_t的3個(gè)數(shù)組中,第6個(gè)成員就指向第6個(gè)HTTP模塊的create_main_conf、create_srv_conf、 create_loc_conf方法建立的結(jié)構(gòu)體,固然,如果相應(yīng)的回調(diào)方法沒(méi)有實(shí)現(xiàn),該指針就為NULL空指針。 */ /* 見(jiàn)ngx_init_cycle conf.ctx = cycle->conf_ctx; //這樣下面的ngx_conf_param解析配置的時(shí)候,里面對(duì)conf.ctx賦值操作,實(shí)際上就是對(duì)cycle->conf_ctx[i] 可如何由ngx_cycle_t核心結(jié)構(gòu)體中找到main級(jí)別的配置結(jié)構(gòu)體呢?Nginx提供的ngx_http_cycle_get_module_main_conf宏可以實(shí)現(xiàn)這個(gè)功能 */ void ****conf_ctx; //有多少個(gè)模塊就會(huì)有多少個(gè)指向這些模塊的指針,見(jiàn)ngx_init_cycle ngx_max_module ngx_pool_t *pool; // 內(nèi)存池 /* 日志模塊中提供了生成基本ngx_log_t日志對(duì)象的功能,這里的log實(shí)際上是在還沒(méi)有履行ngx_init_cycle方法前, 也就是還沒(méi)有解析配置前,如果有信息需要輸出到日志,就會(huì)暫時(shí)使用log對(duì)象,它會(huì)輸出到屏幕。 在ngx_init_cycle方法履行后,將會(huì)根據(jù)nginx.conf配置文件中的配置項(xiàng),構(gòu)造出正確的日志文件,此時(shí)會(huì)對(duì)log重新賦值。 */ //ngx_init_cycle中賦值cycle->log = &cycle->new_log; ngx_log_t *log; //指向ngx_log_init中的ngx_log,如果配置error_log,指向這個(gè)配置后面的文件參數(shù),見(jiàn)ngx_error_log。否則在ngx_log_open_default中設(shè)置 /* 由nginx.conf配置文件讀取到日志文件路徑后,將開(kāi)始初始化error_log日志文件,由于log對(duì)象還在用于輸出日志到屏幕, 這時(shí)候會(huì)用new_log對(duì)象暫時(shí)性地替換log日志,待初始化成功后,會(huì)用new_log的地址覆蓋上面的log指針 */ // 如果沒(méi)有配置error_log則在ngx_log_open_default設(shè)置為NGX_ERROR_LOG_PATH,如果通過(guò)error_log有配置過(guò)則通過(guò)ngx_log_set_log添加到該new_log->next鏈表連接起來(lái) /* 全局中配置的error_log xxx存儲(chǔ)在ngx_cycle_s->new_log,http{}、server{}、local{}配置的error_log保存在ngx_http_core_loc_conf_t->error_log, 見(jiàn)ngx_log_set_log,如果只配置全局error_log,不配置http{}、server{}、local{}則在ngx_http_core_merge_loc_conf conf->error_log = &cf->cycle->new_log; */ //ngx_log_insert插入,在ngx_log_error_core找到對(duì)應(yīng)級(jí)別的日志配置進(jìn)行輸出,由于可以配置error_log不同級(jí)別的日志存儲(chǔ)在不同的日志文件中 ngx_log_t new_log;//如果配置error_log,指向這個(gè)配置后面的文件參數(shù),見(jiàn)ngx_error_log。否則在ngx_log_open_default中設(shè)置 ngx_uint_t log_use_stderr; /* unsigned log_use_stderr:1; */ /* 對(duì)poll,rtsig這樣的事件模塊,會(huì)以有效文件句柄數(shù)來(lái)預(yù)先建立這些ngx_connection t結(jié)構(gòu) 體,以加速事件的搜集、分發(fā)。這時(shí)候files就會(huì)保存所有ngx_connection_t的指針組成的數(shù)組,files_n就是指 針的總數(shù),而文件句柄的值用來(lái)訪問(wèn)files數(shù)組成員 */ ngx_connection_t **files; //sizeof(ngx_connection_t *) * cycle->files_n 見(jiàn)ngx_event_process_init ngx_get_connection /* 從圖9⑴中可以看出,在ngx_cycle_t中的connections和free_connections達(dá)兩個(gè)成員構(gòu)成了1個(gè)連接池,其中connections指向全部連 接池?cái)?shù)組的首部,而free_connections則指向第1個(gè)ngx_connection_t空閑連接。所有的空閑連接ngx_connection_t都以data成員(見(jiàn)9.3.1節(jié))作 為next指針串連成1個(gè)單鏈表,如此,1旦有用戶發(fā)起連接時(shí)就從free_connections指向的鏈表頭獲得1個(gè)空閑的連接,同時(shí)free_connections再指 向下1個(gè)空閑連接。而歸還連接時(shí)只需把該連接插入到free_connections鏈表表頭便可。 */ //見(jiàn)ngx_event_process_init, ngx_connection_t空間和它當(dāng)中的讀寫(xiě)ngx_event_t存儲(chǔ)空間都在該函數(shù)1次性分配好 ngx_connection_t *free_connections;// 可用連接池,與free_connection_n配合使用 ngx_uint_t free_connection_n;// 可用連接池中連接的總數(shù) //ngx_connection_s中的queue添加到該鏈表上 /* 通過(guò)讀操作可以判斷連接是不是正常,如果不正常的話,就會(huì)把該ngx_close_connection->ngx_free_connection釋放出來(lái),這樣 如果之前free_connections上沒(méi)有空余ngx_connection_t,c = ngx_cycle->free_connections;就能夠獲得到剛才釋放出來(lái)的ngx_connection_t 見(jiàn)ngx_drain_connections */ ngx_queue_t reusable_connections_queue;/* 雙向鏈表容器,元素類(lèi)型是ngx_connection_t結(jié)構(gòu)體,表示可重復(fù)使用連接隊(duì)列 表示可以重用的連接 */ //ngx_http_optimize_servers->ngx_http_init_listening->ngx_http_add_listening->ngx_create_listening把解析到的listen配置項(xiàng)信息添加到cycle->listening中 //通過(guò)"listen"配置創(chuàng)建ngx_listening_t加入到該數(shù)組中 ngx_array_t listening;// 動(dòng)態(tài)數(shù)組,每一個(gè)數(shù)組元素貯存著ngx_listening_t成員,表示監(jiān)聽(tīng)端口及相干的參數(shù) /* 動(dòng)態(tài)數(shù)組容器,它保存著nginx所有要操作的目錄。如果有目錄不存在,就會(huì)試圖創(chuàng)建,而創(chuàng)建目錄失敗就會(huì)致使nginx啟動(dòng)失敗。 */ //通過(guò)解析配置文件獲得到的路徑添加到該數(shù)組,例如nginx.conf中的client_body_temp_path proxy_temp_path,參考ngx_conf_set_path_slot //這些配置可能設(shè)置重復(fù)的路徑,因此不需要重復(fù)創(chuàng)建,通過(guò)ngx_add_path檢測(cè)添加的路徑是不是重復(fù),不重復(fù)則添加到paths中 ngx_array_t paths;//數(shù)組成員 nginx_path_t , ngx_array_t config_dump; /* 單鏈表容器,元素類(lèi)型是ngx_open_file_t 結(jié)構(gòu)體,它表示nginx已打開(kāi)的所有文件。事實(shí)上,nginx框架不會(huì)向open_files鏈表中添加文件。 而是由對(duì)此感興趣的模塊向其中添加文件路徑名,nginx框架會(huì)在ngx_init_cycle 方法中打開(kāi)這些文件 */ //該鏈表中所包括的文件的打開(kāi)在ngx_init_cycle中打開(kāi) ngx_list_t open_files; //如nginx.conf配置文件中的access_log參數(shù)的文件就保存在該鏈表中,參考ngx_conf_open_file //創(chuàng)建ngx_shm_zone_t在ngx_init_cycle,在ngx_shared_memory_add也可能創(chuàng)建新的ngx_shm_zone_t,為每一個(gè)ngx_shm_zone_t真正分配同享內(nèi)存空間在ngx_init_cycle ngx_list_t shared_memory;// 單鏈表容器,元素類(lèi)型是ngx_shm_zone_t結(jié)構(gòu)體,每一個(gè)元素表示1塊同享內(nèi)存 ngx_uint_t connection_n;// 當(dāng)前進(jìn)程中所有鏈接對(duì)象的總數(shù),與connections成員配合使用 ngx_uint_t files_n; //每一個(gè)進(jìn)程能夠打開(kāi)的最多文件數(shù) 賦值見(jiàn)ngx_event_process_init /* 從圖9⑴中可以看出,在ngx_cycle_t中的connections和free_connections達(dá)兩個(gè)成員構(gòu)成了1個(gè)連接池,其中connections指向全部連接池?cái)?shù)組的首部, 而free_connections則指向第1個(gè)ngx_connection_t空閑連接。所有的空閑連接ngx_connection_t都以data成員(見(jiàn)9.3.1節(jié))作為next指針串連成1個(gè) 單鏈表,如此,1旦有用戶發(fā)起連接時(shí)就從free_connections指向的鏈表頭獲得1個(gè)空閑的連接,同時(shí)free_connections再指向下1個(gè)空閑連 接。而歸還連接時(shí)只需把該連接插入到free_connections鏈表表頭便可。 在connections指向的連接池中,每一個(gè)連接所需要的讀/寫(xiě)事件都以相同的數(shù)組序號(hào)對(duì)應(yīng)著read_events、write_events讀/寫(xiě)事件數(shù)組, 相同序號(hào)下這3個(gè)數(shù)組中的元素是配合使用的 */ ngx_connection_t *connections;// 指向當(dāng)前進(jìn)程中的所有連接對(duì)象,與connection_n配合使用 /* 事件是不需要?jiǎng)?chuàng)建的,由于Nginx在啟動(dòng)時(shí)已在ngx_cycle_t的read_events成員中預(yù)分配了所有的讀事件,并在write_events成員中預(yù)分配了所有的寫(xiě)事件 在connections指向的連接池中,每一個(gè)連接所需要的讀/寫(xiě)事件都以相同的數(shù)組序號(hào)對(duì)應(yīng)著read_events、write_events讀/寫(xiě)事件數(shù)組,相同序號(hào)下這 3個(gè)數(shù)組中的元素是配合使用的。圖9⑴中還顯示了事件池,Nginx認(rèn)為每個(gè)連接1定最少需要1個(gè)讀事件和1個(gè)寫(xiě)事件,有多少連接就分配多少個(gè)讀、 寫(xiě)事件。怎樣把連接池中的任1個(gè)連接與讀事件、寫(xiě)事件對(duì)應(yīng)起來(lái)呢?很簡(jiǎn)單。由于讀事件、寫(xiě)事件、連接池是由3個(gè)大小相同的數(shù)組組成,所以根據(jù)數(shù)組 序號(hào)便可將每個(gè)連接、讀事件、寫(xiě)事件對(duì)應(yīng)起來(lái),這個(gè)對(duì)應(yīng)關(guān)系在ngx_event_core_module模塊的初始化進(jìn)程中就已決定了(參見(jiàn)9.5節(jié))。這3個(gè)數(shù)組 的大小都是由cycle->connection_n決定。 */ ngx_event_t *read_events;// 指向當(dāng)前進(jìn)程中的所有讀事件對(duì)象,connection_n同時(shí)表示所有讀事件的總數(shù) ngx_event_t *write_events;// 指向當(dāng)前進(jìn)程中的所有寫(xiě)事件對(duì)象,connection_n同時(shí)表示所有寫(xiě)事件的總數(shù) /* 舊的ngx_cycle_t 對(duì)象用于援用上1個(gè)ngx_cycle_t 對(duì)象中的成員。例如ngx_init_cycle 方法,在啟動(dòng)早期, 需要建立1個(gè)臨時(shí)的ngx_cycle_t對(duì)象保存1些變量, 再調(diào)用ngx_init_cycle 方法時(shí)就能夠把舊的ngx_cycle_t 對(duì)象傳進(jìn)去, 而這時(shí)候old_cycle對(duì)象就會(huì)保存這個(gè)前期的ngx_cycle_t對(duì)象。 */ ngx_cycle_t *old_cycle; ngx_str_t conf_file;// 配置文件相對(duì)安裝目錄的路徑名稱(chēng) 默許為安裝路徑下的NGX_CONF_PATH,見(jiàn)ngx_process_options ngx_str_t conf_param;// nginx 處理配置文件時(shí)需要特殊處理的在命令行攜帶的參數(shù),1般是-g 選項(xiàng)攜帶的參數(shù) ngx_str_t conf_prefix; // nginx配置文件所在目錄的路徑 ngx_prefix 見(jiàn)ngx_process_options ngx_str_t prefix; //nginx安裝目錄的路徑 ngx_prefix 見(jiàn)ngx_process_options ngx_str_t lock_file;// 用于進(jìn)程間同步的文件鎖名稱(chēng) ngx_str_t hostname; // 使用gethostname系統(tǒng)調(diào)用得到的主機(jī)名 在ngx_init_cycle中大寫(xiě)字母被轉(zhuǎn)換為小寫(xiě)字母 };

配置解析

配置解析接口

ngx_init_cycle 提供的是配置解析接口。接口是1個(gè)切入點(diǎn),通過(guò)少許代碼提供1個(gè)完全功能的調(diào)用。配置解析接口分為兩個(gè)階段,1個(gè)是準(zhǔn)備階段,另外一個(gè)就是真正開(kāi)始調(diào)用配置解析。準(zhǔn)備階段指甚么呢?主要是準(zhǔn)備3點(diǎn):

  • 準(zhǔn)備內(nèi)存:nginx會(huì)根據(jù) 以往的經(jīng)驗(yàn)(old_cycle)預(yù)測(cè)這1次的配置 需要分配多少內(nèi)存
if (old_cycle->shared_memory.part.nelts) { n = old_cycle->shared_memory.part.nelts; for (part = old_cycle->shared_memory.part.next; part; part = part->next) { n += part->nelts; } } else { n = 1; } if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; }
  • 準(zhǔn)備毛病日志:nginx啟動(dòng)可能出錯(cuò),出現(xiàn)就要記錄就要記錄在日志文件中 。
log = old_cycle->log; pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (pool == NULL) { return NULL; } pool->log = log; cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); if (cycle == NULL) { ngx_destroy_pool(pool); return NULL; } cycle->pool = pool; cycle->log = log; cycle->old_cycle = old_cycle;
  • 準(zhǔn)備數(shù)據(jù)結(jié)構(gòu),1個(gè)是ngx_cycle_t結(jié)構(gòu),1個(gè)是ngx_conf_t結(jié)構(gòu),ngx_cycle_t結(jié)構(gòu)用于寄存所有CORE模塊的 配置,而ngx_conf_t則是用于寄存解析配置的上下文的信息
    ngx_conf_t結(jié)構(gòu)體
struct ngx_conf_s { //當(dāng)前解析到的命令名 char *name; //當(dāng)前命令的所有參數(shù) ngx_array_t *args; //使用的cycle ngx_cycle_t *cycle; //所使用的內(nèi)存池 ngx_pool_t *pool; //這個(gè)pool將會(huì)在配置解析終了后釋放。 ngx_pool_t *temp_pool; //這個(gè)表示將要解析的配置文件 ngx_conf_file_t *conf_file; //配置log ngx_log_t *log; //主要為了提供模塊的層次化(后續(xù)會(huì)詳細(xì)介紹) void *ctx; //模塊類(lèi)型 ngx_uint_t module_type; //命令類(lèi)型 ngx_uint_t cmd_type; //模塊自定義的handler ngx_conf_handler_pt handler; //自定義handler的conf char *handler_conf; };
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; } } conf.ctx = cycle->conf_ctx; // cycle是ngx_cycle_t結(jié)構(gòu),conf就是ngx_conf_t結(jié)構(gòu) conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; //注意,1開(kāi)始命令的類(lèi)型就是MAIN,并且模塊類(lèi)型是core conf.cmd_type = NGX_MAIN_CONF;

準(zhǔn)備好了這些內(nèi)容,nginx開(kāi)始調(diào)用配置解析模塊,其代碼以下:

if (ngx_conf_param(&conf) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } /*開(kāi)始解析文件 然后來(lái)看ngx_conf_parse,這個(gè)函數(shù)第2個(gè)是將要解析的文件名,不過(guò)這里還有1個(gè)要注意的,那就是第2個(gè)參數(shù)可以為空的,如果為空,則說(shuō)明將要解析的是block中的內(nèi)容或param。*/ if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }

第1個(gè)if解析nginx命令行參數(shù)’-g’加入的配置。第2個(gè)if解析nginx配置文件。好的設(shè)計(jì)就體現(xiàn)在接口極度簡(jiǎn)化,模塊之間的耦合非常低。這里只使用區(qū)區(qū)10行完成了配置的解析

配置解析

配置解析模塊在 ngx_conf_file.c 中實(shí)現(xiàn)。模塊提供的接口函數(shù)主要是 ngx_conf_parse,另外模塊提供單獨(dú)的接口ngx_conf_param,用來(lái)解析命令行傳遞的配置,固然這個(gè)接口也是 對(duì)ngx_conf_parse的包裝。

ngx_conf_parse 函數(shù)支持3種不同的解析環(huán)境:


  • parse_file:解析配置文件
  • parse_block:解析塊配置。塊配置1定是由“ {”和“ }”包裹起來(lái)的;
  • parse_param:解析命令行配置。命令行配置中不支持塊指令。

這是1個(gè)遞歸的進(jìn)程。nginx首先解析core模塊的配置。core模塊提供1些塊指令,這些指令引入其他類(lèi)型的模塊,nginx遇到這些指令,就重新迭代解析進(jìn)程,解析其他模塊的配置。這些模塊配置中又有1些塊指令引入新的模塊類(lèi)型或指令類(lèi)型,nginx就會(huì)再次迭代,解析這些新的配置類(lèi)型。比如上圖,nginx遇到“events”指令,就重新調(diào)用ngx_conf_parse()解析event模塊配置,解析完以后ngx_conf_parse()返回,nginx繼續(xù)解析core模塊指令,直到遇到“http”指令。nginx再次調(diào)用ngx_conf_parse()解析http模塊配置的http級(jí)指令,當(dāng)遇到“server”指令時(shí),nginx又1次調(diào)用ngx_conf_parse()解析http模塊配置的server級(jí)指令。
parseconfig
// ngx_conf_parse()解析配置分成兩個(gè)主要階段,1個(gè)是詞法分析,1個(gè)是指令解析。 char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) { char *rv; ngx_fd_t fd; ngx_int_t rc; ngx_buf_t buf; ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, parse_param } type; #if (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; prev = NULL; #endif if (filename) { /* open configuration file */ ................................................ } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) { //到這里說(shuō)明接下來(lái)解析的是block中的內(nèi)容 type = parse_block; } else { //參數(shù) type = parse_param; } for ( ;; ) { rc = ngx_conf_read_token(cf); //此法分析,類(lèi)似文件內(nèi)容格式分析 /* * 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_OK || rc == NGX_CONF_BLOCK_START */ //如果有handler,則調(diào)用handler if (cf->handler) { //handler是自定義解析函數(shù)指針 /* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */ rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; } //沒(méi)有handler則調(diào)用默許解析函數(shù) rc = ngx_conf_handler(cf, rc); if (rc == NGX_ERROR) { goto failed; } } failed: rc = NGX_ERROR; done: .................................... return NGX_CONF_OK; }

為何需要cf的兩個(gè)主要cf->handler和cf->handler_conf這兩個(gè)屬性,其中handler是自定義解析函數(shù)指針,handler_conf是conf指針。

Nginx的配置文件是分塊的,然后event, http都是1個(gè)大的core模塊,然后core模塊中包括了很多2級(jí)模塊(epoll/kqeue/proxy..).也就是1級(jí)模塊中必須包括1個(gè)上下文用來(lái)保存2級(jí)模塊的配置。而在HTTP模塊中又有1些特殊,那就是HTTP模塊中每一個(gè)指令都可能會(huì)有3個(gè)作用域,那就是main/server/loc,所以在HTTP的上下文中,必須同時(shí)保存這3個(gè)上下文。

同時(shí)Nginx中的命令 有

#define NGX_CONF_ARGS_NUMBER 0x000000ff #define NGX_CONF_BLOCK 0x00000100 #define NGX_CONF_FLAG 0x00000200 #define NGX_CONF_ANY 0x00000400 #define NGX_CONF_1MORE 0x00000800 #define NGX_CONF_2MORE 0x00001000 #define NGX_DIRECT_CONF 0x00010000 #define NGX_MAIN_CONF 0x01000000 #define NGX_ANY_CONF 0x1F000000 #define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */ #define NGX_HTTP_MAIN_CONF 0x02000000 #define NGX_HTTP_SRV_CONF 0x04000000 #define NGX_HTTP_LOC_CONF 0x08000000 #define NGX_HTTP_UPS_CONF 0x10000000 #define NGX_HTTP_SIF_CONF 0x20000000 #define NGX_HTTP_LIF_CONF 0x40000000 #define NGX_HTTP_LMT_CONF 0x80000000

DIRECT_CONF顧名思義,就是說(shuō)直接存取CONF,也就是說(shuō)進(jìn)入命令解析函數(shù)的同時(shí),CONF已創(chuàng)建好了,只需要直接使用就好了(也就是會(huì)有create_conf回調(diào))。而Main_conf就是說(shuō)最頂層的conf,比如HTTP/EVENT/PID等等,可以看到都屬屬于CORE 模塊。而NGX_HTTP_XXX就是所有HTTP模塊的子模塊.

ngx_conf_handler函數(shù)

//如果設(shè)置了type if (!(cmd->type & NGX_CONF_ANY)) { //首先判斷參數(shù)個(gè)數(shù)是不是合法 if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) { if (cf->args->nelts < 2) { goto invalid; } ................................................. } /* set up the directive's configuration context */ conf = NULL; //最核心的地方, if (cmd->type & NGX_DIRECT_CONF) { //我們還記得最開(kāi)始ctx是包括了所有core模塊的conf(create_conf回調(diào)),因此這里取出對(duì)應(yīng)的模塊conf. conf = ((void **) cf->ctx)[ngx_modules[i]->index]; // NgX_DIRECT_CONF 才有create_conf回調(diào) } else if (cmd->type & NGX_MAIN_CONF) { //如果不是DIRECT_CONF并且是MAIN,則說(shuō)明我們需要在配置中創(chuàng)建自己模塊的上下文(也就是需要進(jìn)入2級(jí)模塊) conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); // MAIN_CONF1般沒(méi)有create_conf回調(diào) } else if (cf->ctx) { //否則進(jìn)入2級(jí)模塊處理(后續(xù)會(huì)詳細(xì)介紹)。 confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } //調(diào)用命令的回調(diào)函數(shù)。 rv = cmd->set(cf, cmd, conf); if (rv == NGX_CONF_OK) { return NGX_OK; } if (rv == NGX_CONF_ERROR) { return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", name->data, rv); return NGX_ERROR; }

上面代碼中2級(jí)模塊解析那部份先放1下,首先來(lái)看Nginx中帶2級(jí)模塊的1級(jí)模塊如何解析命令的,來(lái)看HTTP模塊(event模塊基本1樣)的解析代碼。

/可以看到?jīng)]有direct_conf,由于http包括有2級(jí)模塊。 static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_block, 0, 0, NULL }, ngx_null_command }; static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; ngx_uint_t mi, m, s; ngx_conf_t pcf; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t **cscfp; ngx_http_core_main_conf_t *cmcf; /* the main http context */ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } //最核心的地方,可以看到修改了傳遞進(jìn)來(lái)的conf *(ngx_http_conf_ctx_t **) conf = ctx; /* count the number of the http modules and set up their indices */ ngx_http_max_module = 0; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } //然后保存了對(duì)應(yīng)模塊的索引. ngx_modules[m]->ctx_index = ngx_http_max_module++; } /* the http main_conf context, it is the same in the all http contexts */ //創(chuàng)建HTTP對(duì)應(yīng)的conf,由于每一個(gè)級(jí)別(main/ser/loc)都會(huì)包括模塊的conf. ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->main_conf == NULL) { return NGX_CONF_ERROR; } /* * the http null srv_conf context, it is used to merge * the server{}s' srv_conf's */ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->srv_conf == NULL) { return NGX_CONF_ERROR; } /* * the http null loc_conf context, it is used to merge * the server{}s' loc_conf's */ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->loc_conf == NULL) { return NGX_CONF_ERROR; } /* * create the main_conf's, the null srv_conf's, and the null loc_conf's * of the all http modules */ .................................... //保存當(dāng)前使用的cf,由于我們只是在解析HTTP時(shí)需要改變當(dāng)前的cf, pcf = *cf; //保存當(dāng)前模塊的上下文 cf->ctx = ctx; .......................................... /* parse inside the http{} block */ //設(shè)置模塊類(lèi)型和命令類(lèi)型 cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; //開(kāi)始解析,這里注意傳遞進(jìn)去的文件名是空 rv = ngx_conf_parse(cf, NULL); if (rv != NGX_CONF_OK) { goto failed; } /* * init http{} main_conf's, merge the server{}s' srv_conf's * and its location{}s' loc_conf's */ ......................................... /* * http{}'s cf->ctx was needed while the configuration merging * and in postconfiguration process */ //回復(fù)cf *cf = pcf; ...................................... return NGX_CONF_OK; failed: *cf = pcf; return rv;

每一個(gè)級(jí)別都會(huì)保存對(duì)應(yīng)的ctx(main/ser/loc),怎樣說(shuō)呢,就是在解析HTTP main中創(chuàng)建了3個(gè)ctx(main/srv/loc),而在HTTP srv block中將會(huì)創(chuàng)建2個(gè)ctx(main/srv/loc),這時(shí)候產(chǎn)生重復(fù)了,那就需要merge了。比如1個(gè)命令(srv_offset)在HTTP main中有1個(gè),那末Nginx將會(huì)把它放入到HTTP main的ctx的srv ctx中,然后server block也有1個(gè),那末Nginx會(huì)繼續(xù)把它放到Server ctx的 srv_conf中,最后merge他們。

所以我們解析1下server模塊

{ ngx_string("server"), NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS, ngx_http_core_server, 0, 0, NULL }, static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; void *mconf; ngx_uint_t i; ngx_conf_t pcf; ngx_http_module_t *module; struct sockaddr_in *sin; ngx_http_conf_ctx_t *ctx, *http_ctx; ngx_http_listen_opt_t lsopt; ngx_http_core_srv_conf_t *cscf, **cscfp; ngx_http_core_main_conf_t *cmcf; ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } http_ctx = cf->ctx; //main conf不變 ctx->main_conf = http_ctx->main_conf; /* the server{}'s srv_conf */ //創(chuàng)建新的srv和loc conf. ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->srv_conf == NULL) { return NGX_CONF_ERROR; } /* the server{}'s loc_conf */ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->loc_conf == NULL) { return NGX_CONF_ERROR; } ............................ /* the server configuration context */ cscf = ctx->srv_conf[ngx_http_core_module.ctx_index]; cscf->ctx = ctx; cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; //保存所有的servers,可以看到是保存在main中的。這模樣最后在HTTP main中就能夠取到這個(gè)srv conf. cscfp = ngx_array_push(&cmcf->servers); if (cscfp == NULL) { return NGX_CONF_ERROR; } *cscfp = cscf; /* parse inside server{} */ //解析,可以看到設(shè)置type為srv_conf. pcf = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_SRV_CONF; rv = ngx_conf_parse(cf, NULL); // 調(diào)用ngx_conf_parse函數(shù) //恢復(fù)cf. *cf = pcf; ........................ } return rv; }

現(xiàn)在來(lái)分析上述代碼

struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //conf就是對(duì)應(yīng)的上下文偏移.比如NGX_HTTP_LOC_CONF_OFFSET ngx_uint_t conf; ngx_uint_t offset; void *post; }; ............................ else if (cf->ctx) { //獲得對(duì)應(yīng)的1級(jí)模塊的2級(jí)上下文(HTTP的 srv_offset) confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { //然后取出對(duì)應(yīng)的模塊conf. conf = confp[ngx_modules[i]->ctx_index]; } }

接下來(lái) 1些簡(jiǎn)單的命令如何使用和配置,主要 看這幾個(gè)數(shù)據(jù)結(jié)構(gòu)

typedef struct { void **main_conf; void **srv_conf; void **loc_conf; } ngx_http_conf_ctx_t; // 下面這些就是放到ngx_command_t的conf域,可以看到就是對(duì)應(yīng)conf的偏移 #define NGX_HTTP_MAIN_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, main_conf) #define NGX_HTTP_SRV_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, srv_conf) #define NGX_HTTP_LOC_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, loc_conf) // //下面就是如何來(lái)取模塊的配置 #define ngx_http_get_module_main_conf(r, module) \ (r)->main_conf[module.ctx_index] #define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index] #define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index] #define ngx_http_conf_get_module_main_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] #define ngx_http_conf_get_module_srv_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] #define ngx_http_conf_get_module_loc_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index] #define ngx_http_cycle_get_module_main_conf(cycle, module) \ (cycle->conf_ctx[ngx_http_module.index] ? \ ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \ ->main_conf[module.ctx_index]: \ NULL)

總結(jié)

其實(shí)不是所有的模塊像http有3個(gè)級(jí)別(main/srv/loc)比如stream和mail模塊只有兩個(gè)級(jí)別(main/srv)),但是整體解析流程都是 1致的,所以學(xué)習(xí)了1個(gè)模塊,就能夠很清楚其他模塊,只是具體的handler不1樣而已。接下來(lái)的博客將要介紹全部Nginx的全部框架 的流程

生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 香蕉国产成版人视频在线观看 | 嫩草影院久久国产精品 | 国产72av国片精品jk制服 | 日本动漫免费看 | 午夜在线播放免费人成无 | 国产高清精品一级毛片 | 女人一级毛片免费观看 | 欧美色图一区二区 | 欧美视频一区二区在线观看 | 一本免费视频 | 亚欧人成精品免费观看 | 中文字幕免费观看 | 性生活一级毛片 | 老司机福利在线观看 | 亚州精品永久观看视频 | 波多野结衣免费观看视频 | 狍和美女一级aa毛片 | 亚洲依依成人精品 | 午夜影院404 | 日本大片免费一级 | 噜噜影院无毒不卡 | 午夜视频高清在线aaa | 又污又黄又无遮挡的网站国产 | 成人欧美一区二区三区在线 | 青青草原手机在线视频 | 一级a毛片免费观看久久精品 | 欧美片xxxx | 精品久久久久久久一区二区伦理 | 欧美性猛交xxxx黑人 | 欧美日韩国产超高清免费看片 | 欧美亚洲高清日韩成人 | 456在线 | 亚洲国产日韩欧美综合久久 | 97精品一区二区三区在线不卡 | 性一交一乱一伦 | 久久久久久一级毛片免费野外 | 亚洲影院手机版777点击进入影院 | 欧美日韩乱码毛片免费观看 | 最近的中文字幕免费完整 | 一级做a爰性色毛片免费 | 日本成人免费在线视频 |