【nginx流程分析之指针使用】
写在前面
这个篇章主要是为了解释nginx的一些指针的使用,以及为什么要这么使用。同时也是为了更好的理解整个流程。如果有希望理解整个的可以看nginx之流程分析
ngx_init_cycle之配置文件
首先来看第一个地方,也就是ngx_cycle_s的这个结构体
初始化conf_ctx
从图片中可以看出来,这个ngx_cycle_s中定义了一个类型为void,然后名称为conf_ctx的四层指针,然后来看一下初始化的地方:
这一段代码是在 /nginx-1.14.2/src/core/ngx_cycle.c 中的ngx_init_cycle方法中。然后ngx_pcalloc是分析了一段178个8字节(void *)的连续内存。这个ngx_pcalloc返回的是void * 。因为conf_ctx是四层指针,ngx_pcalloc返回的是一层指针,所以conf_ctx实际就是一层指针。如图所示
这里我们假设开始的地址是0x13b8175c0。
问题
问:第一个地址是0x13b8175c0,怎么获取第二个内存地址?
答: cycle->conf_ctx +1。这个也是比较简单,直接移动指针就好了。
conf_ctx赋值
然后看接下来nginx的操作,就是对conf_ctx进行操作
结合注释我们看出来,其实就是初始化nginx的 ngx_core_conf_t(主配置文件),然后因为NGX_CORE_MODULE的index也就是cycle->modules[i]->index =0,然后就是把conf_ctx的第一个地址指向了ngx_core_conf_t的地址。
注意:这边的 cycle->conf_ctx[cycle->modules[i]->index] = rv; 并不是把cycle->conf_ctx的地址进行了覆盖(当然内存地址是固定,也不可能覆盖对于物理内存来说),而是指向了rv的地址,然后就是如图所示:
所以我们可以看到其实ctx->conf_ctx是一个二层指针。然后我们继续看nginx的操作。
初始化ngx_conf_s的ctx
首先我们看一下ngx_conf_s的结构体。因为cycle是ngx_conf_s的一个成员,可以看到
然后注意这边的一个ctx,定义类型void *的类型。然后看nginx的操作,
然后可以看到把cycle_ctx的四层指针,赋值给了ctx的这个一层指针。不着急我们还是根据上图分析,这里我们不要把几层指针难到了,我们只要注意是这个指针是指向哪里就好了,然后我们继续看对应的关系图:
然后可以看出来ctx=conf_ctx,所以其实ctx也同时指向了0x13b8175c0这个地址,也就是128块地址的开始的地址。
问题
接下来做几个问题帮助理解:
得到ctx首地址
答案1 conf.ctx。
答案2 &((void **)conf.ctx)[0]。 这个答案可能看起来有点蒙,这个对我们后面看nginx的指针很有帮助,所以我们慢慢分析:
2.1 先类型转换
因为conf.ctx指向了0x13b8175c0的地址,而0x13b8175c0是指向了rv。所以conf.ctx是一个二级指针,所以用(void **) 进行类型转换。
2.2 [0]的意义
根据我们的分析,这个是指向了0x13b8175c0的指向的地方,也就是rv的地址,也就是 0x13b8180f0。 然后再用&取地址,所以我们又回到了0x13b8175c0的地址。
获取rv的地址
如果看懂上面也就很简单了,答案就是*((void **)conf.ctx),对(void **)conf.ctx) 进行取值
获取配置
这个方法在 /nginx-1.14.2/src/core/ngx_conf_file.c中的ngx_conf_handler方法。
((void **) cf->ctx)[cf->cycle->modules[i]->index]
然后我们先看看conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
然后这边cf->cycle->modules[i]->index 是等于0的,
然后这个结合上面可以看出,是已经初始化了,就是 cycle->conf_ctx[cycle->modules[i]->index] = rv。然后conf就是rv;地址是0x13b8180f0
&(((void **) cf->ctx)[cf->cycle->modules[i]->index])
然后看看 conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
这个就比较的特殊了,前面多加了一个取地址的&符号,这个我们慢慢分析。首先是(((void **) cf->ctx)[cf->cycle->modules[i]->index]);这个其实和上面的一样,是指向了cf-ctx的cf->cycle->modules[i]->index所指向的地址,然后这个和上面的不一样,并没有初始化,因为需要加一个&。
打个比方我们ctx->conf_ctx的第一个地址是0x13b8175c0,然后第二个地址是0x13b8175c8,因为集合我们上面的图看,这个地址没有任何指向,所以加一个&重新指向了自己。
然后我们可以看到,这边就是用(void **)转换成了二级指针,然后再去判断是否初始化。因为通常来说没有初始化,所以(void **) conf = null
*(void **) ((char *) cf->ctx + cmd->conf)
这个和上面的两个一样的道理。这个留给读者思考哈