nginx 命令解析

zhaocheng 3年前
   <p>阅读了一段时间的nginx,中间也有很多坑,把自己阅读得到的东西写出来也算是一个笔记吧。</p>    <h2>命令解析</h2>    <pre>  - nginx的命令解析是通过ngx_conf_s来扫描文件然后通过匹配命令来配合ngx_command_s来实现的。我们先来看一下ngx_commands;</pre>    <h3>ngx_command_s</h3>    <pre>  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;//一个指针 可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理  };</pre>    <p>说明</p>    <pre>  其实可以发现 command命令的数据结构特别简单,一般来说都是用来保存配置文件里面的信息</pre>    <ol>     <li>name: 就是是命令的名字,也是写在配置文件中的命令,ngx_conf_s 读取完成之后就是靠这个来找到正确的命令回调函数</li>     <li>type: 这个是代表命令的类型和接受参数的个数。(位置就是在哪个括号的下面 参数个数就是后面有多少个值)</li>     <li>set:回调函数 cf:就是解析器, cmd 当前的命令,conf 当前cmd需要的配置(conf的获得与当前命令的类型息息相关,后面会有)</li>     <li>conf :当前配置项存储的内存位置,因为在nginx的规则里,当前cmd的conf是保存在他包含他的模块里面(main特殊点)所以当一个配置里面有很多小项的时候,允许进行二次偏移。</li>     <li>offset: 一般来说是使用nginx的内置读取函数的时候会用到,直接定位到信息存储的位置</li>     <li>post: 一般在自定义函数中使用,比如保存一个函数指针来对快要过时的命令进行警告</li>    </ol>    <p>#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } 构造一个空的command 一般用于结束标记</p>    <p>type含有的值:</p>    <ol>     <li>NGX_DIRECT_CONF 单纯用来指定配置存储区的寻址方法,只用于core模块</li>     <li>NGX_MAIN_CONF 有两重含义,其一是指定指令的使用上下文是main(其实还是指core模块),其二是指定配置存储区的寻址方法</li>     <li>NGX_ANY_CONF 哪里都行</li>     <li>NGX_CONF_BLOCK 代表着是一个块 就是{}包着</li>     <li>一些模块内的比如NGX_EVENT_CONF 都是在块解析的时候改变模块信息</li>     <li>接受参数的个数 NGX_CONF_NOARGS代表后面没有参数,NGX_CONF_MAX_ARGS 参数的最大个数也就是八个 NGX_CONF_TAKE1 .... NGX_CONF_TAKE7分布代表一到七个参数。还支持组合(NGX_CONF_TAKE1|NGX_CONF_TAKE7)表示接受一个或七个参数</li>    </ol>    <h3>ngx_conf_s</h3>    <pre>  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;  };</pre>    <p>解析流程</p>    <ol>     <li>在cycle里面配置当前的解析上下文 <pre>  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的返回信息       }   }</pre> </li>    </ol>    <pre>  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;</pre>    <p>在解析开始的时候只允许是main的出现在解析命令中 其实这个时候nginx只对属于是ngx_core_module的模块进行了create_conf操作,也是就是只有core模块才初始化了存放配置信息的内存,也只允许是core模块的出现在main_conf</p>    <ol>     <li>读取文件 后调用static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf)来一个一个的读取命令,读取到的内容放在args里面</li>     <li>在不是NGX_CONF_BLOCK_START的情况下,如果配置了自定义处理的handler 就交由handler处理,否则就调用static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)来处理.</li>    </ol>    <p>来看下核心的处理方法</p>    <pre>  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;  }</pre>    <p>一般来说block里面都是调用改变上下文后 调用ngx_conf_parse递归处理,然后处理完成之后还原到原来的状态。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/8e4469234520</p>    <p> </p>