美文网首页
Nginx源码学习——配置项结构体与指针

Nginx源码学习——配置项结构体与指针

作者: 丹丘生___ | 来源:发表于2018-09-27 19:52 被阅读0次

    Nginx中定义了许多基本数据结构,如双向链表ngx_queue_t、动态数组ngx_array_t等等。也定义了与功能息息相关的复杂的数据结构,其中最核心的有:ngx_cycle_t ngx_module_t ngx_command_t等。这里我们着重分析ngx_cycle_t结构体成员——void ****conf_ctx 在 ngx_init_cycle函数执行完成后,其所指向的内存的一系列变化。


    conf_ctx是一个指向配置项结构体的指针。之所以有4个****,是因为该指针指向一个存储着指针的数组,这个数组中的指针元素又有可能指向另一个存放指针的数组。具体结构图将在后文中给出。

    1. conf_ctx指向指针数组

    分配一块连续内存(数组),用于保存ngx_max_module个指针元素,每个指针元素按位置对应一个模块数组中的模块,指向对应于该模块的配置项结构体或者配置项结构体指针数组。ngx_max_module为总的模块个数。cycle->conf_ctx保存数组首地址,代码如下:
    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); //ngx_init_cycle()函数

    之后的内存结构如图所示,本人通过gdb调试取得,本次conf_ctx地址为:0x101011a28

    图1:未保存任何地址的数组 .png
    2.回调核心模块create_conf函数

    接下来,遍历模块数组cycle->modules,找到核心模块,回调其create_conf函数,创建配置项结构体,将相应地址存放于图1中的数组内。

    摘自ngx_init_cycle函数

        for (i = 0; cycle->modules[i]; i++) {
            if (cycle->modules[i]->type != NGX_CORE_MODULE) {
                continue;
            }
            module = cycle->modules[i]->ctx;
    
            if (module->create_conf) {
                rv = module->create_conf(cycle);
                if (rv == NULL) {
                    ngx_destroy_pool(pool);
                    return NULL;
                }
                cycle->conf_ctx[cycle->modules[i]->index] = rv;
            }
        }
    

    cycle->conf_ctx[cycle->modules[i]->index] = rv;语句的执行结果,如图2所示:

    图2:部分数组元素保存了有效地址.png

    图2与图1对比可见,只有少部分数组元素保存了指向配置项结构体的有效地址。比如cycle->conf_ctx[0]保存了指向ngx_core_module模块的配置项结构体指针,cycle->conf_ctx[4]理应保存指向ngx_event_module模块结构体指针数组的指针,但现在是0x0.
    那么这些位置的数组元素是在何时保存有效指针的呢? 答案是:解析配置文件的时候。


    3. 解析配置文件,处理配置项及其它

    除了解析配置文件,处理配置项,还有可能创建下一级的指针数组。

    代码如下所示,:

        //`摘自ngx_init_cycle函数`
        if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
            environ = senv;
            ngx_destroy_cycle_pools(&conf);
            return NULL;
        }
    

    conf实参中保存了conf_ctxcycle变量的副本。进入·ngx_conf_parse·函数内部,略过诸多细节,看关键性代码:

    回调模块自身注册的配置处理函数,
    rv = (*cf->handler)(cf, NULL, cf->handler_conf); //cf 即 conf实参
    或者,调用通用的配置处理函数:
    rc = ngx_conf_handler(cf, rc);

    假设调用ngx_conf_handler函数,进入该函数内部:

     if (cmd->type & NGX_DIRECT_CONF) {
        conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
    
        } else if (cmd->type & NGX_MAIN_CONF) {
             conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
    
        } else if (cf->ctx) {
              confp = *(void **) ((char *) cf->ctx + cmd->conf);
    
              if (confp) {
                  conf = confp[cf->cycle->modules[i]->ctx_index];
              }
        }
    

    解析一下上面的代码。
    conf是该函数定义的ngx_conf_t类型的局部变量,cmd变量的类型是ngx_command_t
    我们知道,定义一个模块,首先就是实现ngx_module_t结构体,该结构体内就有一个ngx_command_t数组成员,该成员决定了该模块对那些配置项感兴趣,以及如何处理自己感兴趣的配置项,换言之——定制功能。

    举例1——ngx_events_module模块,其ngx_command_t数组成员如下:

    static ngx_command_t  ngx_events_commands[] = {
    
        { ngx_string("events"), // 配置项名称
          NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, //配置项类型
          ngx_events_block, //处理配置项的函数
          0,
          0,
          NULL },
    
          ngx_null_command
    };
    

    可以看到,ngx_events_module模块只对event{...}块配置项感兴趣,并且使用ngx_events_block函数处理该配置项。

    举例2——ngx_core_module模块,其ngx_command_t数组成员如下:

    static ngx_command_t  ngx_core_commands[] = {
    ......
    ......
        { ngx_string("worker_processes"),
          NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
          ngx_set_worker_processes, 
          0,
          0,
          NULL },
    ......
    ......
    }
    

    cmd->type的取值中: NGX_MAIN_CONFNGX_DIRECT_CONF功能很难用语言清晰总结,我们直接看其功能:

    • cmd->type值的组成中有NGX_DIRECT_CONF参与时(比如ngx_core_module模块),局部变量conf取值如下:
      conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
      cf->ctx 也就是前文中conf_ctx的副本,cf->cycle->modules[i]->index是模块在模块数组中的索引,前文中已经描述了模块数组与conf_ctx所指向数组的一一对应关系。
      由于void*不能被解引用,所以这里转换为void **,假设是cf->cycle->modules[i]得到是ngx_core_module模块,那么有index==0,因此等价于:
      conf = conf_ctx[0]; //即 0x0000000101012580,如图2所示

    • 当cmd->type值的组成中无NGX_DIRECT_CONF参与,有NGX_MAIN_CONF参与时(如ngx_events_module模块),局部变量conf取值如下:
      conf =& (((void **) cf->ctx)[cf->cycle->modules[i]->index]);
      对于ngx_events_module模块,已知cf->cycle->modules[i]->index == 4(如图2),那么对 conf就是对conf_ctx[4]取地址后的值。
      所以有如下示意图:

      图3:指向指针数组某元素的指针conf.png

    接下来将通过ngx_command_t的函数指针set,回调单个配置项处理函数。如:
    ngx_core_modulengx_set_worker_processes函数处理worker_processes配置项
    ngx_events_modulengx_events_block函数处理events{...}块配置项。

    并且将conf指针传入被回调函数内。

    对于ngx_core_module,观察ngx_core_commands[]内的一系列配置项处理函数,不难得出,conf直接指向其配置项结构体,因此有图4:

    图4:数组元素作为指针直接指向配置项结构体.png

    对于ngx_events_module模块,分析其ngx_events_block函数:

      ctx = ngx_pcalloc(cf->pool, sizeof(void *)); 
    ...
      *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    ...
       *(void **) conf = ctx;
    

    第一句:为指针ctx分配了内存空间
    第二句:分配一个ngx_event_max_module大小的指针数组,ngx_event_max_moduleevents子模块的数目;令ctx指向该数组。
    第三句:将ctx的值赋给conf指向的数组元素(如图3),也就是令该数组元素内保存的指针指向第二句中分配的指针数组。
    因此有如图5:

    图5:创建events模块的配置项结构体指针数组

    接下来,执行过程类似于本文中的2、3步,该函数内部又将调用create_conf创建事件子模块配置项结构体,正如开始时创建核心模块配置项结构体那般,并把地址保存于图5中下一个指针数组中的相应位置,然后调用ngx_conf_parse函数解析配置文件,处理配置项。
    结果如图6所示:

    图6: events模块.png

    在执行守护程序之前,在gdb中查看内存:x/100xg ngx_cycle->conf_ctx,打印信息如下,可见仍有许多单元没有被存入有效地址。

    0x101011a28:    0x0000000101012580  0x0000000000000000
    0x101011a38:    0x0000000000000000  0x0000000101012678
    0x101011a48:    0x00000001010132f0  0x0000000000000000
    0x101011a58:    0x0000000000000000  0x0000000101013430
    0x101011a68:    0x0000000000000000  0x0000000000000000
    0x101011a78:    0x0000000000000000  0x0000000000000000
    0x101011a88:    0x0000000000000000  0x0000000000000000
    0x101011a98:    0x0000000000000000  0x0000000000000000
    0x101011aa8:    0x0000000000000000  0x0000000000000000
    0x101011ab8:    0x0000000000000000  0x0000000000000000
    0x101011ac8:    0x0000000000000000  0x0000000000000000
    0x101011ad8:    0x0000000000000000  0x0000000000000000
    0x101011ae8:    0x0000000000000000  0x0000000000000000
    

    未完待续。。

    相关文章

      网友评论

          本文标题:Nginx源码学习——配置项结构体与指针

          本文链接:https://www.haomeiwen.com/subject/kyasoftx.html