nginx的所有配置结构体全部放在一个cycle的四级指针中,本文就具体分析一下每级指针究竟指向的是什么,下图表示了这个四级指针每一级的指向,图中虚拟地址模拟了真实地址,ctx指针存的指向虚拟地址为1的数据,图中只列举出coreeventhttp模块最基础的配置结构。
图解Nginx 中的4级指针-LMLPHP

     需要注意得是,从图中不难发现,对于常用的模块,core模块,event模块,http模块,core模块只需要2级指针就能搞定,event模块按照逻辑只需要3级指针,而http模块需要4级指针。event按照常理缺少了http模块中间那个虚拟地址1000的结构体(区分mainconfserv conflocconf),需要3级指针就行。但是nginx为了方便配置,也在event模块中间也加入了类似的一个结构体指针(虚拟地址75存储100 的指针),不过这个指针直接指向了event模块的指针。这样event模块和http模块一样也形成了4级指针,这样设计event模块我认为是为了兼容http模块配置才形成了4级指针。


    从代码切入,看一下ctx的初始化过程。

  1. cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));

     这段代码初始化了cyclecontextconf_ctx指向一段空间,每个地址存储void *的指针,这个指针能指向任何的一段空间,也就是各种conf结构体。

     以下代码为第一层指针赋值,也就是每个conf_ctx每个空间存储第一层conf的结构体的首地址,这类conf的结构体不会包含eventhttp模块的,因为nginx并不确定配置一定有eventhttp模块的配置,所以在conf文件中没有碰到eventhttp的时候,是不会建立对应的结构体的。

  1. for (i = 0; ngx_modules[i]; i++) {
  2.         if (ngx_modules[i]->type != NGX_CORE_MODULE) {     //加载core module,log module等
  3.             continue;
  4.         }

  5.         module = ngx_modules[i]->ctx;

  6.         if (module->create_conf) {
  7.             rv = module->create_conf(cycle);              //rv 是conf结构体首地址
  8.             if (rv == NULL) {
  9.                 ngx_destroy_pool(pool);
  10.                 return NULL;
  11.             }
  12.             cycle->conf_ctx[ngx_modules[i]->index] = rv; //conf_ctx每个空间存储conf的首地址,构造了两层指针
  13.         }
  14.     }

      下面的代码是解析command,获得这个commandconf结构体


  1. if (cmd->type & NGX_DIRECT_CONF) {
  2.                 conf = ((void **) cf->ctx)[ngx_modules[i]->index]; //1

  3.             } else if (cmd->type & NGX_MAIN_CONF) {
  4.                 conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); //2

  5.             } else if (cf->ctx) {
  6.                 confp = *(void **) ((char *) cf->ctx + cmd->conf); //3

  7.                 if (confp) {
  8.                     conf = confp[ngx_modules[i]->ctx_index];
  9.                 }
  10.             }

  11.             rv = cmd->set(cf, cmd, conf);

     1.NGX_DIRECT_CONF表示ngx_core_module,也就是配置中最外边配置的那些参数,因为 ngx_core_module,所对应的conf结构体,在初始化的时候已经创建好了,所以再ctx[index]的位 置是有conf的首地址的指针的,相当于图中虚拟地址50,后面在set command到结构体的时候,只需要把这个指针传过去就行了,这里ctx 2级指针是说明ctx一级指针指向的存储conf的首地址虚拟地址1ctx二级指针指向conf的首地址,相当于图中虚拟地址50
     2.
NGX_MAIN_CONF表示eventshttp的配置,也就是说在cycle初始化的时候并不会初始化这些conf,前面说了因为我们根本不确定这些配置项是否真的有。所以在碰到events 或者http那种配置项的时候,那么就是初始化这一类配置项的时候,首先需要在ctx的相应位置赋值他们coreconfmodule的结构体。因为之前ctx在这个位置并没有任何指向结构体的指针。所以把ctx位置的指针(虚拟地址2)赋值给conf,让后面的set函数,为这个位置赋值配置项结构体的首地址。

图解Nginx 中的4级指针-LMLPHP

     对于第三个位置注释的代码说明,应该还是先说说event模块的加载,下面的代码是nginxevents模块set函数

  1. ctx = ngx_pcalloc(cf->pool, sizeof(void *)); //建立一个指针,虚拟地址75 (为了兼容http模块的4级指针的指针,图中红色的位置)
  2.     if (ctx == NULL) {
  3.         return NGX_CONF_ERROR;
  4.     }

  5.     *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *)); //可以看到 event模块位置被赋值了一段空间,相当于ctx指向虚拟地址100,也就是虚拟地址75存储了100,相当于图中标黄色的过程1
  6.     if (*ctx == NULL) {
  7.         return NGX_CONF_ERROR;
  8.     }

  9.     *(void **) conf = ctx; //所以这里conf相当于虚拟地址2的地方存储是首地址为75的指针,相当于图中标红的过程2

  10.     for (i = 0; ngx_modules[i]; i++) {
  11.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
  12.             continue;
  13.         }

  14.         m = ngx_modules[i]->ctx;

  15.         if (m->create_conf) {
  16.             (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle); //相当于图中标绿的过程3
  17.             if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {               //相当于地址为100的位置存储首地址600,600是event core module的位置,虚拟地址101的地方存储首地址700,700是event epoll模块的结构体的首地址
  18.                 return NGX_CONF_ERROR;
  19.             }
  20.         }
  21.     }

  22.     pcf = *cf;
  23.     cf->ctx = ctx; //重新赋值虚拟地址75的指针,继续解析配置。
  24.     cf->module_type = NGX_EVENT_MODULE;
  25.     cf->cmd_type = NGX_EVENT_CONF;

  26.     rv = ngx_conf_parse(cf, NULL);

      从上面的代码可以看出event模块的四级指针确实有些牵强,完全可以省略虚拟地址75位置的指针,虚拟地址2的位置直接存储100,不省略的原因正是前面说的原因,为了兼容http模块的配置,再看下http的代码
图解Nginx 中的4级指针-LMLPHP


  1. ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  2.     if (ctx == NULL) {
  3.         return NGX_CONF_ERROR;
  4.     }

  5.     *(ngx_http_conf_ctx_t **) conf = ctx; //相当于图中标红的过程1

  6.     ngx_http_max_module = 0;
  7.     for (m = 0; ngx_modules[m]; m++) {
  8.         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
  9.             continue;
  10.         }

  11.         ngx_modules[m]->ctx_index = ngx_http_max_module++;
  12.     }

  13.     ctx->main_conf = ngx_pcalloc(cf->pool,
  14.                                  sizeof(void *) * ngx_http_max_module); //相当于图中标黄的过程2
  15.     if (ctx->main_conf == NULL) {
  16.         return NGX_CONF_ERROR;
  17.     }
  18.     ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  19.     if (ctx->srv_conf == NULL) {
  20.         return NGX_CONF_ERROR;
  21.     }

  22.     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  23.     if (ctx->loc_conf == NULL) {
  24.         return NGX_CONF_ERROR;
  25.     }

  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); //相当于图中标绿色的过程3
  34.             if (ctx->main_conf[mi] == NULL) {
  35.                 return NGX_CONF_ERROR;
  36.             }
  37.         }

      看完了上面http模块的初始化,应该就了解前面代码的用意:

  1.   confp = *(void **) ((char *) cf->ctx + cmd->conf);

  2.   if (confp) {
  3.          conf = confp[ngx_modules[i]->ctx_index];
  4.              }

     在初始化完event模块之后,cf->ctx存储的就是75,event模块的cmd->conf都是0,因为这个cf->ctx一个兼容指针,所以confp指向的就是虚拟地址100,那么后面的代码根据模块的ctx_index就可以得到conf 了。

     对于http模块cf->ctx存储的是1000,那么cmd->conf对于http模块不是0,在结构体ngx_http_conf_ctx_tmainsrvloc有不同的位移,后面的逻辑和event一致,所以event模块的四级指针只是为了兼容http模块而多设了一层指针,那层指针充当了http指向ngx_http_conf_ctx_t结构体作用,这层对event模块是没有什么用的,对于http模块来说可以区分是mainsrvloc的配置。



09-02 10:32