f_ctx =ngx_pcalloc(pool, ngx_max_module * sizeof(void *));你看到了吗?ngx_max_module是系统中所有module的总数,conf_ctx在这一句中用来给每个module占一个位置(一个指针),但是每个位置到底指向啥东西呢?呵呵,反正是个void *,指啥都行,可能对于有的module,我们要拿到一个我们想要的实体,要透过4层的指针(即void ****),你是不是感到很好奇呢?
兜了一圈,我们发现最终这些指针数组的成员赋值都会在这里的第4步来完成。
这里分3种情况:
(1) NGX_DIRECT_CONF
对于那些游离于{}之外的配置,一般属于ngx_core_conf_t的配置内容,在ngx_init_cycle的时候已经对NGX_CORE_MODULE类型的模块进行了初始化(模块需要有init函数),这里根据配置信息,set函数会做配置结构内中成员的赋值。
(2) NGX_MAIN_CONF
这样的配置包括event,http等,他们没有init函数,所以实际的空间分配需要在set函数内完成,于是就有了:
conf =&(((void **) cf->ctx)[ngx_modules[i]->index]); //取指针的地址
rv =cmd->set(cf, cmd, conf); // 指针在函数内被赋值
set中conf参数是一个二重指针,这也就有个之后在ngx_http_block中的语句:
ctx =ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
*(ngx_http_conf_ctx_t **) conf = ctx;
(3)其他
其实这里的“其他”,主要是一些server和location的类型的command,这些command大量的集中于http的相关配置中。
我们先来看ngx_http_conf_ctx_t结构:
typedefstruct {
void **main_conf;
void **srv_conf;
void **loc_conf;
}ngx_http_conf_ctx_t;
这里有一点需要交代的是,像http模块下,有一些所谓的子模块,而这些子模块基本上都是http中server或者location中的一些配置。这些配置通过模块中ctx_index,以数组的形式,将他们的配置结构的指针保存在srv_conf或loc_conf中,这就是他们的类型为什么会是void **。
我们来看这句:
confp =*(void **) ((char *) cf->ctx + cmd->conf);
我相信这一句是比较难懂的,实际上cmd->conf是成员在结构体中的偏移量。这里举个例子就明白了:
假设我们在32位机下,cmd->conf为8,cf->ctx的地址为0x008004A2,那么cf->ctx+cmd->conf =
0x008004A2+ 8 = 0x008004AA,你应该晓得这个地址里面放的是什么,所以外面做了一个类型强转,(void **),这个转换告诉我们,0x008004AA是个二级指针值,这个地址下放的是一个指针,现在我们需要里面的指针,于是就有了*(void **)。
完整的过程是这样的:
//得到一个指针,到底 几维呢?无所谓,这里我们是拿它当二维指针来用的,为啥呢?
// 前面说过了,他们是子模块配置的指针的数组!
confp =*(void **) ((char *) cf->ctx + cmd->conf);
conf = confp[ngx_modules[i]->ctx_index]; // 拿到配置结构
rv =cmd->set(cf, cmd, conf); // 做处理
最后说明一下,开始提到的那个void ****类型成员,使用这种类型的原因我们可以在ngx_events_block函数中找到答案。