Nginx学习之ngx_cycle_s中的四维指针

        好记性不如烂笔头!刚开始看完一遍nginx的源码,由于源码工程量比较大,看完之后理解了,过一段时间再回过头来看又有点遗忘。所以决定做一下记录,以便后续翻阅,如有漏错,欢迎指正。

        ngx_cycle_s结构体可以说是Nginx中最为重要的结构体,它贯穿着整个进程代码。本节我就该结构中的 void ****conf_ctx 四级指针进行深入学习。

        Nginx的所有配置结构体全部放在conf_ctx这个四维指针中。下来我们梳理一下源码,主要对HTTP模块所对应的四维指针进行详细分析,其它模块同理,再次就不再做赘述。下来先看一下源码中的ngx_init_cycle函数,主要代码如下:

ngx_cycle_t* ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ……
	// conf_ctx指针 指向ngx_max_module个 (void *) 指针指向每个模块
    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
    if (cycle->conf_ctx == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
    }

    ……
	// conf_ctx指针中 modules 二级指针指向 ngx_modules 全局变量
    if (ngx_cycle_modules(cycle) != NGX_OK) {
        ngx_destroy_pool(pool);
        return NULL;
    }

	// 只有core模块才有create_conf, init_conf结构
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

		// 指向各CORE模块的 module context
		// 对应http模块来说指向&ngx_http_module_ctx
        module = cycle->modules[i]->ctx;

        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }

			// 每个模块create_conf返回 void * 对应的 conf_ctx[i] 中
            cycle->conf_ctx[cycle->modules[i]->index] = rv;
        }
    }
	……

	// conf的ctx指向cycle中的四维指针conf_ctx
    conf.ctx = cycle->conf_ctx;
    conf.cycle = cycle;
    conf.pool = pool;
    conf.log = log;
    conf.module_type = NGX_CORE_MODULE;
    conf.cmd_type = NGX_MAIN_CONF;
	……
	
	// 解析配置文件
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }
	……
}

        上述代码实现的conf_ctx这个四维指针中的其中两维。

        1、一维是申请并指向所有模块(ngx_max_module个 (void *) 指针)的配置,而并非所有主模块的配置。

        2、二维指向对应主模块的create_conf函数(只有ngx_core_module模块该函数指针不为NULL)所返回的内存。对应http模块则对应的create_conf为NULL,因此它的二维目前指的为NULL

        结合上述代码的后半段及以下代码分析,ngx_conf_handler函数是ngx_conf_parse解析配置文件时调用的。主要代码如下:

static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
	……
	if (cmd->type & NGX_DIRECT_CONF)     // 主模块 ngx_core_commands 中命令
    {
		conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
	} 
    else if (cmd->type & NGX_MAIN_CONF)   // 子模块event、http、mail 中命令
    {
		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];
		}
	}
	
    // 调用对应命令的set回调函数
	rv = cmd->set(cf, cmd, conf);
	……
}

        上述代码中cf->ctx指向cycle中的四维指针conf_ctx;上述我们已将思维指针的其中二维已经分析即:所有模块的一维指针已初始化,ngx_core_module模块的二维指针已初始化(其他event、http、mail 对应的create_conf函数指针都为NULL)。

        下来分析ngx_conf_handler函数。因为 http命令字的type为 :NGX_MAIN_CONF,因此当遇到http{}关键字时,对应会执行:conf =&(((void **) cf->ctx)[cf->cycle->modules[i]->index]),我们下来分析一下该语句。

        当命令类型为 NGX_DIRECT_CONF 时,说明该命令为 ngx_core_module 模块命令,因为ngx_core_module 模块有 create_conf 函数,此时:conf_ctx中的一维指针中的 void* 已经通过core模块的create_conf函数申请了空间,所以只需将conf指针指向申请的空间就行。对应执行:conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index]。

        当命令类型为 NGX_MAIN_CONF时,对应所以的子模块(event、http、mail)都没有实现create_conf函数,因此需取出void*的地址来通过各自模块的ngx_xxx_block函数为其申请空间。对应执行:conf = &((void **) cf->ctx)[cf->cycle->modules[i]->index]。

        然后将conf指针作为cmd->set()函数的回调函数的参数并执行。对于http命令来说将调用nginx_http_block函数,主要代码如下:

static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	……
    if (*(ngx_http_conf_ctx_t **) conf) {
        return "is duplicate";
    }

    // 申请 ngx_http_conf_ctx_t 结构的内存
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    // *conf 实质等于 数组申请的 void*
    *(ngx_http_conf_ctx_t **) conf = ctx;

    // 以下3个ngx_pcalloc申请 ngx_http_conf_ctx_t 结构中的三个二维指针内存
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }

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

    // 调用对应http模块的create_main_conf、create_srv_conf、create_loc_conf函数
    for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[m]->ctx;
        mi = cf->cycle->modules[m]->ctx_index;

        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_loc_conf) {
            ctx->loc_conf[mi] = module->create_loc_conf(cf);
            if (ctx->loc_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }
    ……
}

        如上代码,对应http模块来说,它的二维指针指向的结构为:ngx_http_conf_ctx_t;该结构中有三个 void** 类型的指针;对于main_conf、srv_conf、loc_conf三个void**指针申请内存为: ngx_http_max_modulevoid*的大小;因此http模块的三维指针对应:main_conf、srv_conf、loc_conf类型的指针。因为这三个指针每一个都是二维指针(void**),指向的一个void* 的数组,对于main_conf指针来说指向对应http模块的create_main_conf函数返回的地址,因此http模块的四维指针对应的是create_xxx_conf函数返回的地址。执行srv_conf、loc_conf指针同理在此不再赘述,下来看一下 http 的第一个模块ngx_http_core_module 的 create_main_conf 函数,主要代码如下:

static void * ngx_http_core_create_main_conf(ngx_conf_t *cf)
{
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t));
    if (cmcf == NULL) {
        return NULL;
    }
    ……

    return cmcf;
}

        以上对四维指针详细的分析了,其实对于四维指针只有http模块用了四维;对于ngx_core_module 模块只用了二维指针,源代码其实实现的非常清楚。下来我通过一张图片对本节进行一下整体梳理,梳理图如下:

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值