Nginx源码分析模块指令加载

上文写到了Nginx配置文件读取以及解析,本文介绍模块的指令加载。还是以前文的nginx_move_domain_cache为例

server {
        listen 80;
        server_name www.firefoxbug.net;
        gzip on;
 
        location /mv_cache{
                mv_cache;
        }
        ....
}

上面有一模块mv_cache,这个模块里面的指令是mv_cache,参数是0个。再比如server_name也是一个指令,参数是一个"www.firefoxbug.net"。调用前文的ngx_conf_parse读取配置文件,然后调用handler函数进行解析,对于普通的处理(普通指令),会调用ngx_conf_handler,这里再回顾下模块的指令数组定义结构
typedef struct ngx_command_s {
    ngx_str_t             name;	//指令名字
    ngx_uint_t            type;	//指令所属模块类型,预设参数,指令出现的位置等
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);	//指令处理函数,存储到相应的模块结构体
    ngx_uint_t            conf;	//去内存里面找相应的模块,一般都是NGX_HTTP_MODULE
    ngx_uint_t            offset;//该配置项的精确存放位置,一般都是偏移。用offsetof(A, b)宏来表示A结构体中的b偏移量
    void                 *post;
}ngx_command_t;

对应mv_cache模块的指令数组定义:
/* Commands */
static ngx_command_t  ngx_http_move_domain_cache_commands[] = {
    { ngx_string("mv_cache"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_move_domain_cache,
      0,
      0,
      NULL },
    ngx_null_command
};

下面看普通指令处理函数
src/core/ngx_conf_file.c -->> ngx_conf_handler()

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{

	// 获取指令的名字,这里是读取配置文字得到的
	name = cf->args->elts;

	// 遍历每个模块,检查指令(比如这里就是nginx_move_domain_cache)是在哪个模块中被定义,并且进行处理
	for (i = 0; ngx_modules[i]; i++) {

		/* look up the directive in the appropriate modules */

		if (ngx_modules[i]->type != NGX_CONF_MODULE
			&& ngx_modules[i]->type != cf->module_type)
		{
			continue;
		}

		//指令必须以NULL结尾的原因
		cmd = ngx_modules[i]->commands;
		if (cmd == NULL) {
			continue;
		}
		//每个模块command数组
		//遍历模块的command,比较名字,然后调用回调函数set
		for ( /* void */ ; cmd->name.len; cmd++) {
			// 这里先比较name(配置文件中的命令)和cmd->name(模块中定义的命令)的长度,再比较内容
			if (name->len != cmd->name.len) {
				continue;
			}

			if (ngx_strcmp(name->data, cmd->name.data) != 0) {
				continue;
			}

			/* is the directive's location right ? */

			//类型是否匹配
			if (!(cmd->type & cf->cmd_type)) {
				if (cmd->type & NGX_CONF_MULTI) {
					multi = 1;
					continue;
				}

				goto not_allowed;
			}

			/* is the directive's argument count right ? */

			if (!(cmd->type & NGX_CONF_ANY)) {

				if (cmd->type & NGX_CONF_FLAG) {

					if (cf->args->nelts != 2) {
						goto invalid;
					}

				} else if (cmd->type & NGX_CONF_1MORE) {

					if (cf->args->nelts < 2) {
						goto invalid;
					}

				} else if (cmd->type & NGX_CONF_2MORE) {

					if (cf->args->nelts < 3) {
						goto invalid;
					}

				} else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {

					goto invalid;

				} else if (!(cmd->type & argument_number[cf->args->nelts - 1]))
				{
					goto invalid;
				}
			}

			/* set up the directive's configuration context */

			conf = NULL;
			
			//最核心的地方,

			if (cmd->type & NGX_DIRECT_CONF) {
				//ctx是包含了所有core模块的conf(create_conf回调),因此这里取出对应的模块conf.
				conf = ((void **) cf->ctx)[ngx_modules[i]->index];

			} else if (cmd->type & NGX_MAIN_CONF) {
				//如果不是DIRECT_CONF并且是MAIN,则说明我们需要在配置中创建自己模块的上下文(也就是需要进入二级模块)
				conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);

			} else if (cf->ctx) {
				//否则进入二级模块处理
				confp = *(void **) ((char *) cf->ctx + cmd->conf);

				if (confp) {
					conf = confp[ngx_modules[i]->ctx_index];
				}
			}

			//调用对应命令的处理函数,这个回调函数根据 ngx_command_s 预设的,可以是自定义的参数处理函数,也可是调用Nginx提供的。
			//比如worker_process这命令在nginx.c中对应的处理函数是ngx_conf_set_num_slot,这里的set就是ngx_conf_set_num_slot
			//这里还可以通过这个函数把本模块注册到NGX_HTTP_CONTENT_PHASE阶段
			rv = cmd->set(cf, cmd, conf);

		}
	}
}

对于具体的模块指令配置结构体存储,以后的文章再讨论。
上面就是指令的加载,这里做一个大概的总结:每一个模块定义的同时,会指定模块相应的指令数组,以及每个指令的处理函数。Nginx在初始化过程中,一旦读取到配置文件的指令配置(这里好像叫token)。就会去遍历相应模块,具体为从预先定义,编译的模块数组找出对应模块,然后根据模块的指令数组,一旦token长度和字符串和指令数组的某一项匹配,就会调用该指令的回调处理函数。通过这个函数可以1.处理相应的参数,保存到模块配置结构体里 ;2.把本模块注册到NGX_HTTP_CONTENT_PHASE阶段,这样,一旦请求匹配到对应的location,就调用模块处理。

标签:Linux, Nginx

评论已关闭