https://blog.youkuaiyun.com/brainkick/article/details/7554206
https://www.cnblogs.com/chengxuyuancc/p/3792258.html
https://www.cnblogs.com/my_life/articles/5239028.html
ngx_events_block :
//二级指针存储了一个地址: (地址为三级指针) ***p,
//既二级指针指向了一个三级指针.
*(void **) conf = ctx;
ngx_http_block :
//ctx是个一级指针,以及指针又指向了内部的( 三个并列的) 二级指针,
//所以ctx这里可以理解为三级指针.
*(ngx_http_conf_ctx_t **) conf = ctx;
在nginx中有个四级指针,void **** conf_ctx,那么这个四级的指针在什么情况下用到呢?
答案可以在events block中找到。
在处理events这个指令时,它在conf_ctx中某个地方占住一个位置(指针看成数组),以index代表此处的下标。
在ngx_events_block函数中,先会申请一个指针,即代码中的ctx:
ctx = ngx_pcalloc(cf->pool, sizeof(void *));
这个指针后面就会放到index下标处。那么这个指针ctx,指向的是什么呢?那是个数组,各种event的配置的结构指针数组,从c语言上看,假设我们用一个void **tmp的二级指针来指向这个数组:
tmp = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
*ctx = tmp;
我们看到此时ctx就是一个指向二级指针的指针,即void ***,而这个ctx又是conf_ctx的一个成员,很显然,conf_ctx被冠以void ****也理所当然。
而evnents是个MAIN_CONF(关于什么是MAIN_CONF,自己去查查这里就不说了),所以在解析时(即ngx_conf_handler),取到该index所在位置的地址:
conf = &(((void **) cf->ctx)[ngx_modules[i]->index])
这个conf,通过后面:
cmd->set(cf, cmd, conf);
即调用ngx_events_block时,通过:
*(void **) conf = ctx;
完成了index位置上面指针的放置,这个有C语言基础的同学都知道的,在函数内部给外面指针参数赋值的原理。
现在的问题是为什么非要用void ****,void *不可以吗?从语言层面来说,是可以的。我个人认为,把指针层次写全,对用户(或者是代码逻辑上层)来说,容易看到它的指向和层次。而写成void *,那么只是在实际使用者,根据需要时候来做类型强转,在其他的地方,看不出它的层次。
以上是我个人的理解,有其他见解的朋友欢迎一起讨论。
/// Nginx 四级指针 详解二/
https://www.cnblogs.com/chengxuyuancc/p/3792258.html
校验工作完成后,Nginx将指令名和所有模块预定义支持的指令进行对比,找到完全匹配的配置指令定义。根据配置指令的不同类型,配置项的存储位置也不同。
NGX_DIRECT_CONF类型的配置指令,其配置项存储空间是全局作用域对应的存储空间。这个类型的指令主要出现在ngx_core_module模块里。
1 conf = ((void **) cf->ctx)[ngx_modules[i]->index];
NGX_MAIN_CONF表示配置指令的作用域为全局作用域。纵观Nginx整个代码,除了ngx_core_module的配置指令(同时标识为NGX_DIRECT_CONF)位于这个作用域中外,另外几个定义新的子级作用域的指令–events、http、mail、imap,都是非NGX_DIRECT_CONF的NGX_MAIN_CONF指令,它们在全局作用域中并未被分配空间,所以在指令处理函数中分配的空间需要挂接到全局作用域中,故传递给指令处理函数的参数是全局作用域的地址。
1 conf = &(((void **) cf->ctx)[ngx_modules[i]->index];
其它类型配置指令项的存储位置和指令出现的作用域(并且非全局作用域)有关:
1 confp = *(void **) ((char *) cf->ctx + cmd->conf);
2 if (confp) { conf = confp[ngx_modules[i]->ctx_index]; }
配置项将要存储的位置确定后,调用指令处理函数,完成配置项初始化和其它工作。
rc = cmd->set(cf, cmd, conf);
下面通过分析模块HTTP的初始化来进一步加深理解模块的初始化。