国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
nginx配置信息的解析流程
2011年9月9日admin發(fā)表評論閱讀評論 1,744 次瀏覽
請關(guān)注最新修正合訂:http://lenky.info/ebook/
這一系列的文章還是在09年寫的,存在電腦里很久了,現(xiàn)在貼出來。順序也不記得了,看到那個就發(fā)那個吧,最近都會發(fā)上來。歡迎轉(zhuǎn)載,但請保留鏈接:http://lenky.info/,謝謝。
nginx的配置文件格式是nginx作者自己定義的,并沒有采用像語法分析生成器LEMON那種經(jīng)典的LALR(1)來描述配置信息,這樣做的好處就是自由,而壞處就是對于nginx的每一項配置信息都必須自己去解析,因此我們很容易看到nginx模塊里大量篇幅的配置信息解析代碼,比如模塊ngx_http_core_module。
當然,nginx配置文件的格式也不是隨意的,它有自己的一套規(guī)范:
nginx配置文件是由多個配置項組成的。每一個配置項都有一個項目名和對應(yīng)的項目值,項目名又被稱為指令(Directive),而項目值可能簡單的字符串(以分號結(jié)尾),也可能是由簡單字符串和多個配置項組合而成配置塊的復雜結(jié)構(gòu)(以大括號}結(jié)尾),因此我們可以將配置項歸納為兩種:簡單配置項和復雜配置項。
從上面這條規(guī)范可以看到這里包含有遞歸的思想,因此在后面的配置解析代碼里可以看到某些函數(shù)被遞歸調(diào)用,其原因也就在這里。
對于復雜配置項來說,其值是由多個簡單/復雜配置項組成,因此nginx不做過細的處理,一般就是申請內(nèi)容空間、切換解析狀態(tài),然后遞歸調(diào)用解析函數(shù);真正將用戶配置信息轉(zhuǎn)換為nginx內(nèi)變量的值,還是那些簡單配置項所對應(yīng)的處理函數(shù)。
不管是簡單配置項還是復雜配置項,它們的項目名和項目值都是由標記(token:這里指一個配置文件字符串內(nèi)容中被空格、引號、括號,比如’{‘、換行符等分割開來的字符子串。)組成的,配置項目名就是一個token,而配置項目值可以是一個、兩個、多個token組成。
比如簡單配置項:
daemon off;
其項目名daemon為一個token,項目值off也是一個token。而簡單配置項:
error_page  404   /404.html;
其項目值就包含有兩個token,分別為404和/404.html。
對于復雜配置項:
location /www {
index    index.html index.htm index.php;
}
其項目名location為一個token,項目值是一個token(/www)和多條簡單配置項組成的復合結(jié)構(gòu)。
前面將token解釋為一個配置文件字符串內(nèi)容中被空格、引號、括號,比如’{‘等分割開來的字符子串,那么很明顯,上面例子中的taken是被空格分割出來,事實上下面這樣的配置也是正確的:
“daemon” “off”;
‘daemon’ ‘off’;
daemon ‘off’;
“daemon” off;
當然,一般情況下沒必要這樣費事去加些引號,除非我們需要在token內(nèi)包含空格而又不想使用轉(zhuǎn)義字符(\)的話就可以利用引號,比如:
log_format   main ‘$remote_addr – $remote_user [$time_local]  $status ‘
‘”$request” $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;
但是像下面這種格式就會有問題,這對于我們來說很容易理解,不多詳敘:
“daemon “off”;
對于如此多的配置項,nginx怎樣去解析它們呢?在什么時候去解析呢?事實上,對于nginx所有可能出現(xiàn)的配置項(通過項目名即指令Directive去判斷),nginx都會提供有對應(yīng)的代碼去解析它,如果配置文件內(nèi)出現(xiàn)了nginx無法解析的配置項,那么nginx將報錯并直接退出程序。
舉例來說,對于配置項daemon,在模塊ngx_core_module的命令解析數(shù)組內(nèi)的第一項就是保存的對該配置項進行解析所需要的信息,比如daemon配置項的類型,執(zhí)行實際解析操作的回調(diào)函數(shù),解析出來的配置項值所存放的地址等:
{ ngx_string(“daemon”),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_core_conf_t, daemon),
NULL },
而如果我在配置文件內(nèi)加入如下配置內(nèi)容:
lenky on;
啟動nginx,直接返回錯誤,這是因為對于lenky指令,nginx沒有對應(yīng)的代碼去解析它:
[emerg]: unknown directive “l(fā)enky” in /usr/local/nginx/conf/nginx.conf:2
上面給出的解析daemon配置項的數(shù)據(jù)類型為ngx_command_s結(jié)構(gòu)體類型,該結(jié)構(gòu)體類型對所有的nginx配置項進行了統(tǒng)一的描述:
typedef struct ngx_command_s     ngx_command_t;
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_command_s結(jié)構(gòu)體類型的元素用于解析并獲取一項nginx配置,其中字段name指定獲取的配置項目名稱,字段set指向一個回調(diào)函數(shù),該函數(shù)執(zhí)行解析并獲取配置項值的操作;而type指定該配置項的相關(guān)信息,比如:
1,該配置的類型:NGX_CONF_FLAG表示該配置項目有一個布爾類型的值,例如”daemon”就是一個布爾類型配置項,值為”on”或”off”;NGX_CONF_BLOCK表示該配置項目有一個塊類型的值,比如配置項”http”、”events”等。
2,該配置接收的參數(shù)個數(shù):NGX_CONF_NOARGS、NGX_CONF_TAKE1、NGX_CONF_TAKE2、……、NGX_CONF_TAKE7,分別表示該配置項沒有參數(shù)、一個、兩個、七個參數(shù)。
3,該配置的位置域:NGX_MAIN_CONF、NGX_HTTP_MAIN_CONF、NGX_EVENT_CONF、NGX_HTTP_SRV_CONF、NGX_HTTP_LOC_CONF、NGX_HTTP_UPS_CONF等等。
字段conf被NGX_HTTP_MODULE類型模塊所用,該字段指定當前配置項所在的大致位置,取值為NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET三者之一;其它模塊不用該字段,直接指定為0。
字段offset指定該配置項值的精確存放位置,一般指定為某一個結(jié)構(gòu)體變量的字段偏移。也有那種塊配置項,例如”server”,它不用保存配置項值,或者說無法保存,或者說其值被分得更細小而被保存起來,此時字段offset也指定為0即可。
字段post在大多數(shù)情況下為NULL,但在某些特殊配置項中也會指定值,而且多為回調(diào)函數(shù)指針,例如auth_basic、connection_pool_size、request_pool_size、optimize_host_names、client_body_in_file_only等配置項。
對于配置文件的格式以及配置項在nginx中的封裝基本就描述到這,下面開始對整個nginx配置信息的解析流程進行描述。
假設(shè)我們以命令:
nginx -c /usr/local/nginx/conf/nginx.conf
啟動nginx,而配置文件nginx.conf也比較簡單,如下所示:
worker_processes  2;
error_log  logs/error.log debug;
events {
use epoll;
worker_connections  1024;
}
http {
include       mime.types;
default_type  application/octet-stream;
server {
listen       8888;
server_name  localhost;
location / {
root   html;
index  index.html index.htm;
}
error_page  404              /404.html;
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   html;
}
}
}
下面就來描述nginx是如何將這些配置信息轉(zhuǎn)化為nginx內(nèi)各對應(yīng)變量的值以控制nginx工作的。
首先,抹掉一些細節(jié),我們跟著nginx的啟動流程進入到與配置信息相關(guān)的函數(shù)調(diào)用處:
main–>ngx_init_cycle–>ngx_conf_parse:
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
此處調(diào)用ngx_conf_parse傳入了兩個參數(shù),第一個參數(shù)為ngx_conf_s變量,而第二個參數(shù)就是保存的配置文件路徑字符串“/usr/local/nginx/conf/nginx.conf”。ngx_conf_parse函數(shù)是執(zhí)行配置解析的關(guān)鍵函數(shù),其原型如下:
char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);
它是一個間接的遞歸函數(shù),也就是說雖然我們在該函數(shù)體內(nèi)看不到直接的對其本身的調(diào)用,但是它執(zhí)行的一些函數(shù)(比如ngx_conf_handler)內(nèi)又會調(diào)用ngx_conf_parse函數(shù),因此形成遞歸,這一般在處理一些特殊配置指令或復雜配置項,比如指令include、events、http、server、location等的處理時。
ngx_conf_parse函數(shù)體代碼量不算太多,但是它也將配置內(nèi)容的解析過程分得很清楚,總體來看分成三個步驟:1,區(qū)分當前解析狀態(tài);2,讀取配置標記token;3,當讀取了合適數(shù)量的標記token之后對其進行實際的處理,轉(zhuǎn)換為nginx內(nèi)變量的值。
當執(zhí)行到ngx_conf_parse函數(shù)內(nèi)時,配置的解析可能處于三種狀態(tài):
第一種,剛開始解析一個配置文件,即此時的參數(shù)filename指向一個配置文件路徑字符串,需要函數(shù)ngx_conf_parse打開該文件并獲取相關(guān)的文件信息以便下面代碼讀取文件內(nèi)容并進行解析,除了在上面介紹的nginx啟動時開始主配置文件解析時屬于這種情況,還有當遇到include指令時也將以這種狀態(tài)調(diào)用ngx_conf_parse函數(shù),因為include指令表示一個新的配置文件要開始解析。狀態(tài)標記為type = parse_file;。
第二種,開始解析一個配置塊,即此時配置文件已經(jīng)打開并且也已經(jīng)對文件部分進行了解析,當遇到復雜配置項比如events、http等時,這些復雜配置項的處理函數(shù)又會遞歸的調(diào)用ngx_conf_parse函數(shù),此時解析的內(nèi)容還是來自當前的配置文件,因此無需再次打開它,狀態(tài)標記為type = parse_block;。
第三種,開始解析配置項,這在對用戶通過命令行-g參數(shù)輸入的配置信息進行解析時處于這種狀態(tài),如:
nginx -g ‘daemon on;’
nginx在調(diào)用ngx_conf_parse函數(shù)對配置信息’daemon on;’進行解析時就是這種狀態(tài),狀態(tài)標記為type = parse_param;。
前面說過,nginx配置是由標記組成的,在區(qū)分好了解析狀態(tài)之后,接下來就要讀取配置內(nèi)容,而函數(shù)ngx_conf_read_token就是做這個事情的:
rc = ngx_conf_read_token(cf);
函數(shù)ngx_conf_read_token對配置文件內(nèi)容逐個字符掃描并解析為單個的token,當然,該函數(shù)并不會頻繁的去讀取配置文件,它每次從文件內(nèi)讀取足夠多的內(nèi)容以填滿一個大小為NGX_CONF_BUFFER的緩存區(qū)(除了最后一次,即配置文件剩余內(nèi)容本來就不夠了),這個緩存區(qū)在函數(shù)ngx_conf_parse內(nèi)申請并保存引用到變量cf->conf_file->buffer內(nèi),函數(shù)ngx_conf_read_token反復使用該緩存區(qū),該緩存區(qū)可能有如下一些狀態(tài):
初始狀態(tài),即函數(shù)ngx_conf_parse內(nèi)申請后的初始狀態(tài)。
這是在處理過程中的狀態(tài),有一部分配置內(nèi)容已經(jīng)被解析為一個個token并保存起來,而有一部分內(nèi)容正要被組合成token,還有一部分內(nèi)容等待處理。
這是在字符都處理完了,需要繼續(xù)從文件內(nèi)讀取新的內(nèi)容到緩存區(qū)。前面圖示說過,已解析字符已經(jīng)沒用了,因此我們可以將已掃描但還未組成token的字符移動到緩存區(qū)的前面,然后從配置文件內(nèi)讀取內(nèi)容填滿緩存區(qū)剩余的空間,情況如下:
如果配置文件內(nèi)容不夠,即最后一次,那么情況就是下面這樣:
函數(shù)ngx_conf_read_token在讀取了合適數(shù)量的標記token之后就開始下一步驟即對這些標記進行實際的處理。那多少才算是讀取了合適數(shù)量的標記呢?區(qū)別對待,對于簡單配置項則是讀取其全部的標記,也就是遇到結(jié)束標記分號;為止,此時一條簡單配置項的所有標記都被讀取并存放在cf->args數(shù)組內(nèi),因此可以調(diào)用其對應(yīng)的回調(diào)函數(shù)進行實際的處理;對于復雜配置項則是讀完其配置塊前的所有標記,即遇到大括號{為止,此時復雜配置項處理函數(shù)所需要的標記都已讀取到,而對于配置塊{}內(nèi)的標記將在接下來的函數(shù)ngx_conf_parse遞歸調(diào)用中繼續(xù)處理,這可能是一個反復的過程。
當然,函數(shù)ngx_conf_read_token也可能在其它情況下返回,比如配置文件格式出錯、文件處理完(遇到文件結(jié)束)、塊配置處理完(遇到大括號}),這幾種返回情況的處理都很簡單,不多詳敘。
對于簡單/復雜配置項的處理,一般情況下,這是通過函數(shù)ngx_conf_handler來進行的,而也有特殊的情況,也就是配置項提供了自定義的處理函數(shù),比如types指令。函數(shù)ngx_conf_handler也做了三件事情,首先,它需要找到當前解析出來的配置項所對應(yīng)的ngx_command_s結(jié)構(gòu)體,前面說過該ngx_command_s包含有配置項的相關(guān)信息以及對應(yīng)的回調(diào)實際處理函數(shù)。如果沒找到配置項所對應(yīng)的ngx_command_s結(jié)構(gòu)體,那么誰來處理這個配置項呢?自然是不行的,因此nginx就直接進行報錯并退出程序。其次,找到當前解析出來的配置項所對應(yīng)的ngx_command_s結(jié)構(gòu)體之后還需進行一些有效性驗證,因為ngx_command_s結(jié)構(gòu)體內(nèi)包含有配置項的相關(guān)信息,因此有效性驗證是可以進行的,比如配置項的類型、位置、帶參數(shù)的個數(shù)等等。只有經(jīng)過了嚴格有效性驗證的配置項才調(diào)用其對應(yīng)的回調(diào)函數(shù):
rv = cmd->set(cf, cmd, conf);
進行處理,這也就是第三件事情。在處理函數(shù)內(nèi),根據(jù)實際的需要又可能再次調(diào)用函數(shù)ngx_conf_parse,如此反復直至所有配置信息都被處理完。
下面來看一個set回調(diào)函數(shù)的例子,以對配置指令daemon的解析函數(shù)為例,根據(jù)前面給出的指令daemon對應(yīng)的ngx_command_s結(jié)構(gòu)體可以看到,其set回調(diào)函數(shù)指向的是函數(shù)ngx_conf_set_flag_slot,該函數(shù)的原型如下:
char * ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
這是一個公共的解析函數(shù),即它并不是單獨為解析daemon配置指令而存在,而是對于所有NGX_CONF_FLAG類型的配置項都是用的該函數(shù)來進行解析。
源文件:ngx_conf_file.c
char *
ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char  *p = conf;
ngx_str_t        *value;
ngx_flag_t       *fp;
ngx_conf_post_t  *post;
/* 解析出來的對應(yīng)值存放的內(nèi)存位置 */
fp = (ngx_flag_t *) (p + cmd->offset);
/* 該內(nèi)存位置已有值,故知配置指令重復 */
if (*fp != NGX_CONF_UNSET) {
return “is duplicate”;
}
/* cf->args存放的是與當前處理配置項相關(guān)的各個token,比如解析daemon配置指令時, cf->args內(nèi)的數(shù)據(jù)詳細如下,以便于理解(通過gdb調(diào)試獲得的結(jié)果):
(gdb) p *cf->args
$1 = {elts = 0x9a0c798, nelts = 2, size = 8, nalloc = 10, pool = 0x9a0bf00}
(gdb) p *(ngx_str_t*)(cf->args->elts)
$2 = {len = 6, data = 0x9a0c7e8 “daemon”}
(gdb) p *(((ngx_str_t*)(cf->args->elts)+1))
$3 = {len = 3, data = 0x9a0c7f0 “off”}
*/
value = cf->args->elts;
/* 解析,布爾值的配置很好解析,”on”轉(zhuǎn)為nginx內(nèi)的1,”off”轉(zhuǎn)為0。*/
if (ngx_strcasecmp(value[1].data, (u_char *) “on”) == 0) {
*fp = 1;
} else if (ngx_strcasecmp(value[1].data, (u_char *) “off”) == 0) {
*fp = 0;
} else {   /* 出錯提示 */
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
“invalid value \”%s\” in \”%s\” directive, ”
“it must be \”on\” or \”off\”",
value[1].data, cmd->name.data);
return NGX_CONF_ERROR;
}
/* 其它處理函數(shù),對于daemon配置指令來說為NULL,但是對于其它指令,比如optimize_server_names則還需調(diào)用自定義的處理。*/
if (cmd->post) {
post = cmd->post;
return post->post_handler(cf, post, fp);
}
return NGX_CONF_OK;
}
對于nginx配置文件的解析流程基本就是如此,上面的介紹忽略了很多細節(jié),前面也說過,事實上對于配置信息解析的代碼(即各種各樣的回調(diào)函數(shù)cmd->set的具體實現(xiàn))占去了nginx大幅的源代碼,而我們這里并沒有做過多的分析,僅例舉了daemon配置指令的解析過程,因為對于不同的配置項,解析代碼完全是根據(jù)自身應(yīng)用而不同的,當然,除了一些可公共出來的代碼以外。最后,看一個nginx配置文件解析的流程圖,如下:
nginx自身對字符串進行了封裝,對應(yīng)的封裝結(jié)構(gòu)體為ngx_str_t,這里說明一下,以后類似的情況同此。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Nginx安全配置參考
【Linux網(wǎng)絡(luò)編程】Nginx -- 模塊開發(fā)(基本模塊解析)
OpenResty + ngx_lua_waf使用
nginx的配置系統(tǒng)
handler模塊(100%)
偽靜態(tài)規(guī)則轉(zhuǎn)換之a(chǎn)pache rewrite規(guī)則轉(zhuǎn)nginx rewrite配置
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服