nginx 命令解析
閱讀了一段時間的nginx,中間也有很多坑,把自己閱讀得到的東西寫出來也算是一個筆記吧。
命令解析
- nginx的命令解析是通過ngx_conf_s來掃描文件然后通過匹配命令來配合ngx_command_s來實現的。我們先來看一下ngx_commands;
ngx_command_s
struct ngx_command_s { ngx_str_t name;//command 名字 ngx_uint_t type;//command 類型 接受參數的個數 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //設置數據 ngx_uint_t conf;//指定當前配置項存儲的內存位置 和后面讀取對應 ngx_uint_t offset;//存放信息的offset void *post;//一個指針 可以指向任何一個在讀取配置過程中需要的數據,以便于進行配置讀取的處理 };
說明
其實可以發現 command命令的數據結構特別簡單,一般來說都是用來保存配置文件里面的信息
- name: 就是是命令的名字,也是寫在配置文件中的命令,ngx_conf_s 讀取完成之后就是靠這個來找到正確的命令回調函數
- type: 這個是代表命令的類型和接受參數的個數。(位置就是在哪個括號的下面 參數個數就是后面有多少個值)
- set:回調函數 cf:就是解析器, cmd 當前的命令,conf 當前cmd需要的配置(conf的獲得與當前命令的類型息息相關,后面會有)
- conf :當前配置項存儲的內存位置,因為在nginx的規則里,當前cmd的conf是保存在他包含他的模塊里面(main特殊點)所以當一個配置里面有很多小項的時候,允許進行二次偏移。
- offset: 一般來說是使用nginx的內置讀取函數的時候會用到,直接定位到信息存儲的位置
- post: 一般在自定義函數中使用,比如保存一個函數指針來對快要過時的命令進行警告
#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } 構造一個空的command 一般用于結束標記
type含有的值:
- NGX_DIRECT_CONF 單純用來指定配置存儲區的尋址方法,只用于core模塊
- NGX_MAIN_CONF 有兩重含義,其一是指定指令的使用上下文是main(其實還是指core模塊),其二是指定配置存儲區的尋址方法
- NGX_ANY_CONF 哪里都行
- NGX_CONF_BLOCK 代表著是一個塊 就是{}包著
- 一些模塊內的比如NGX_EVENT_CONF 都是在塊解析的時候改變模塊信息
- 接受參數的個數 NGX_CONF_NOARGS代表后面沒有參數,NGX_CONF_MAX_ARGS 參數的最大個數也就是八個 NGX_CONF_TAKE1 .... NGX_CONF_TAKE7分布代表一到七個參數。還支持組合(NGX_CONF_TAKE1|NGX_CONF_TAKE7)表示接受一個或七個參數
ngx_conf_s
struct ngx_conf_s { char *name; ngx_array_t *args;//解析一行得到的東西 ngx_cycle_t *cycle; ngx_pool_t *pool; ngx_pool_t *temp_pool; ngx_conf_file_t *conf_file;//文件 ngx_log_t *log; void *ctx;//當前的主ctx信息 ngx_uint_t module_type;//模塊的類型 ngx_uint_t cmd_type;//當前是哪類解析 ngx_conf_handler_pt handler; char *handler_conf; };
解析流程
- 在cycle里面配置當前的解析上下文
for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_CORE_MODULE) {//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;//保存core_module的返回信息 } }
conf.ctx = cycle->conf_ctx; conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; conf.cmd_type = NGX_MAIN_CONF;
在解析開始的時候只允許是main的出現在解析命令中 其實這個時候nginx只對屬于是ngx_core_module的模塊進行了create_conf操作,也是就是只有core模塊才初始化了存放配置信息的內存,也只允許是core模塊的出現在main_conf
- 讀取文件 后調用static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf)來一個一個的讀取命令,讀取到的內容放在args里面
- 在不是NGX_CONF_BLOCK_START的情況下,如果配置了自定義處理的handler 就交由handler處理,否則就調用static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)來處理.
來看下核心的處理方法
static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)//config的處理 { char *rv; void *conf, **confp; ngx_uint_t i, found; ngx_str_t *name; ngx_command_t *cmd; name = cf->args->elts;//args的第一個字段保存的是名字信息 found = 0;//是否找到 for (i = 0; ngx_modules[i]; i++) {//其實nginx就是去遍歷所有的模塊 cmd = ngx_modules[i]->commands;//拿到model的command if (cmd == NULL) { continue; } for ( /* void */ ; cmd->name.len; cmd++) { if (name->len != cmd->name.len) { continue; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } found = 1;//代表找到一個名字符合的 if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type)//對自己進行排除 { continue; } /* is the directive's location right ? */ if (!(cmd->type & cf->cmd_type)) { //判斷出現的位置對不對 continue; } if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" is not terminated by \";\"", name->data); return NGX_ERROR; }//對塊解析進行限制 if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" has no opening \"{\"", name->data); return NGX_ERROR; } /* is the directive's argument count right ? */ if (!(cmd->type & NGX_CONF_ANY)) {//參數個數的驗證 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; } } else if (cmd->type & NGX_CONF_2MORE) { if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } /* set up the directive's configuration context */ conf = NULL; //module->index 是代表模塊位置的下標 module->ctx_index代表在他所屬模塊組的下標 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]);//取地址 /* 一般來說core_moudle執行的命令都是進行的block解析,他們屬于一個功能模塊,如果不用的話是不需要進行初始化的,所以都是把指向他配置的指針給他,在模塊真正調用的時候去設置自己的配置文件信息,然后保存回去就好 */ } else if (cf->ctx) {//自定義的存儲區允許進行二次偏移 confp = *(void **) ((char *) cf->ctx + cmd->conf); /* 舉例 :ngx_event_moudle里面 ctx = ngx_pcalloc(cf->pool, sizeof(void *)); *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *)); cf->ctx = ctx; cf->module_type = NGX_EVENT_MODULE; cf->cmd_type = NGX_EVENT_CONF; 這時候cmd->conf =0; 對ctx取* 得到一個數組 長度是ngx_event_max_module 每個的內容是void * 再來取下標 就得到了具體的conf */ if (confp) {//有些模塊沒有配置文件 conf = confp[ngx_modules[i]->ctx_index];//通過組下標找到位置 } } 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; } } if (found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive is not allowed here", name->data); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", name->data); return NGX_ERROR; invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of arguments in \"%s\" directive", name->data); return NGX_ERROR; }
一般來說block里面都是調用改變上下文后 調用ngx_conf_parse遞歸處理,然后處理完成之后還原到原來的狀態。
來自:http://www.jianshu.com/p/8e4469234520
本文由用戶 zhaocheng 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!