Nginx源码分析模块加载

转载请注明出处:http://www.firefoxbug.com/?p=2030

前文说了Nginx里万物皆模块,今天描述下模块的加载,因为模块的加载是一个递归的过程,所以比较难以理解。

模块类型


Nginx官方定义了6种核心模块,定义宏声明是NGX_CORE_MODULE,分别是ngx_core_module,ngx_http_module,ngx_events_module,ngx_mail_module,ngx_errlog_module,ngx_openssl_module。这6种核心模块又可以定义自己全新的模块,比如ngx_events_module重新定义events类型为NGX_EVENT_MODULE,ngx_http_module定义http模块类型NGX_HTTP_MODULE。在上文叙述的ngx_command_s第二个字段就是指定这些模块类型。

模块加载


src/core/nginx.c -->> main()

// 计算模块个数,并且设置各个模块顺序(索引)
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
	ngx_modules[i]->index = ngx_max_module++;
}

cycle = ngx_init_cycle(&init_cycle);

这里会把nginx编译时候./configure的模块都清点了,然后统一放到ngx_modules这个数组里。index代表每个模块在这个数组里的下标,如ngx_core_module就应该是0.

src/core/ngx_cycle.c -->> ngx_init_cycle()

for (i = 0; ngx_modules[i]; i++) {
	if (ngx_modules[i]->type != NGX_CORE_MODULE) {
		continue; //这里只对核心模块NGX_CORE_MODULE进行处理
	}
	
	//得到core modules
	module = ngx_modules[i]->ctx;

	//如果create_conf存在,则直接创建相应模块的配置文件,实际上只有ngx_core_module_create_conf,其余都是NULL
	if (module->create_conf) {
		rv = module->create_conf(cycle); //对每个模块调用模块内部的钩子ngx_xxx_module_create_conf.
		//保存config,这里看到conf_ctx里面就是放对应模块的main conf.
		cycle->conf_ctx[ngx_modules[i]->index] = rv;
	}
}
...
conf.module_type = NGX_CORE_MODULE;//,解析CORE模块
conf.cmd_type = NGX_MAIN_CONF;	//一开始命令的类型就是MAIN(比如"event")
...
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
	environ = senv;
	ngx_destroy_cycle_pools(&conf);
	return NULL;
}
....
//利用配置文件解析出的配置项,初始化上面create_conf生成的模块配置结构体。
if (module->init_conf) {
	if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index]) == NGX_CONF_ERROR)
	{
		environ = senv;
		ngx_destroy_cycle_pools(&conf);
		return NULL;
	}
}

下面着重看ngx_conf_parse这个配置文件分析的函数.
src/core/ngx_conf_file.c -->> ngx_conf_parse()

//循环遍历每一行配置文件,读取配置内容
for ( ;; ) {

	if (filename) {
		// 打开配置文件
		fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
		...
		} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
			type = parse_block;
		} else {
			type = parse_param;
		}
	}
	...
	//读入一个token,一般是一行
	//读到的配置参数放到: (ngx_str_t*)(*((*cf).args)).elts
	rc = ngx_conf_read_token(cf);

	/*
	 * ngx_conf_read_token() may return
	 *
	 *    NGX_ERROR             there is error
	 *    NGX_OK                the token terminated by ";" was found
	 *    NGX_CONF_BLOCK_START  the token terminated by "{" was found
	 *    NGX_CONF_BLOCK_DONE   the "}" was found
	 *    NGX_CONF_FILE_DONE    the configuration file is done
	 */

	/* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
	//判断cf是否有handler回调,如果有的话,优先调用handler回调.这里的意思应该主要是块指令(http{}等)
	//特别说一下,http的模块使用handler是ngx_http_block,具体请看ngx_http.c
	if (cf->handler) {
		// 类似于 http{},这类块指令。
		//使用handler处理
		rv = (*cf->handler)(cf, NULL, cf->handler_conf);
		if (rv == NGX_CONF_OK) {
			continue;
		}
	}

	//否则进入一般处理, 调用ngx_conf_handler对当前的token进行处理
	rc = ngx_conf_handler(cf, rc);
}

模块会去调用预先设置的handler(这里不是很明白,但代码的意思就是这样)或者普通的处理函数ngx_conf_handler。总之,这里的handler会去读取配置文件,然后根据模块的ngx_command_s出吃话对应结构体,具体下文(模块指令加载)分析。这里就拿http模块做为例子,上面的handler会去调用ngx_http_block
src/http/ngx_http.c -->> ngx_http_block()
//http模块相关参数初始化,具体后文详细讲解
...
//设置模块类型和命令类型
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
//继续parse config,这里注意传递进去的文件名是空
rv = ngx_conf_parse(cf, NULL);
...
//接下来就开始递归读取和解析HTTP模块下配置指令。

以此类推,Nginx会解析配置文件,然后对应到模块列表里找,不断递归解析。

标签:C/C++, Linux, Nginx

评论已关闭