nginx 模块架构 -- 配置文件的读取和配置过程

来源:互联网 发布:mac花屏两秒自动恢复 编辑:程序博客网 时间:2024/06/05 11:53
nginx 模块架构 -- 配置文件的读取和配置过程
nginx 程序以模块的形式存在。
模块是一个全局结构变量,如下声明一个模块
例如:
ngx_module_t abc_module
然后你完成 abc_module 的相应功能。
 
nginx 在编译时,生成objs/modules.c 文件
里面有一个ngx_module_t 指针数组,叫ngx_module_t *ngx_modules[]
以空地址为结尾。
新加的module 占据其中的一项
extern ngx_module_t  abc_module;
ngx_module_t *ngx_modules[]={
...
&abc_module,
...
NULL
}
 
 
如此,实现了模块注册, 核心模块便可以调用新添加的模块了。
具体的调用流程:
在函数: ngx_init_cycle(ngx_cycle_t *old_cycle) 中
 module = ngx_modules[i]->ctx;
if (module->create_conf)  
{
  rv = module->create_conf(cycle);
  cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
调用了create_conf(初始化), 并保存了结果。
 
在 ngx_conf_file.c :243 ngx_conf_parse 调用ngx_conf_handler
 
说明: ngx_modules 是静态指针数组。 在objs/ngx_modules.c 中定义
ngx_modules[i]->ctx; 是abc_module.ctx, 根据约定,每一个module 都有自己的ctx 及commands.
module 的结构定义这里忽略,可看源码。 这就是结构化编程的意义。它能够穿插到已有的架构中。
ctx 是什么? 是一个结构,包含模块名称,及函数指针(例如create_conf)
如果create_conf 为非空, 就执行之, create_conf 的作用是分配内存,初始化数据,返回的地址保存在
cycle->conf_ctx[ngx_modules[i]->index] 中   
cycle 是一个进程持有的配置数据结构。 cycle->conf 是在进程内存池中动态分配的指针数组。
 cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));   //ngx_cycle.c 188行
将来把每个模块的配置信息保存在这里。
表示为: cycle->conf_ctx[ngx_modules[i]->index] = rv;
这个复杂的赋值,代表的意思是,返回值保存在一个数组中, 数组的位置就是你模块的顺序号。
这个cycle->conf_ctx, 就是conf.ctx, 这个cycle, 就是conf.cycle
其实,作为核心模块,它是可以知道记录到数组的位置的。简单的实现就用顺序号i, 这里使用ngx_modules[i]->index.
这两个值是相等的,只所以使用复杂方式,是基于只要知道了module 指针,就可以知道module 的所有信息的理念。
所以module 结构中添加了index 数据域。
 
你怎么知道ngx_modules[i]->index 是modules 顺序号呢? 看下面程序,是顺序赋值,代表了模块的顺序号(动态赋值)。
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
    ngx_modules[i]->index = ngx_max_module++;
}
 
这样就把create_conf 的内存用cycle->conf_ctx数组保存了起来
 
 
commands 是什么?  
是你定义的从配置文件取到指令,执行相应的commands, 若需要保存数据,指明保存到ctx什么位置。
若不需要保存参数,可以指明conf位置及其偏移为0
 
 
解析配置文件,每一个token 都要输出,然后在module 的commands 中查找。用暴力搜索的方法
 
for (i = 0; ngx_modules[i]; i++) {
  cmd = ngx_modules[i]->commands;  
  for ( /* void */ ; cmd->name.len; cmd++)  
  {
    if (ngx_strcmp(name->data, cmd->name.data) != 0) {
      continue;
    }
    found = 1;
    ...
  }
  ...
}
 
ngx_module_t  ngx_http_proxy_module = {
...
}
static ngx_command_t  ngx_http_proxy_commands[] = {
 
    { ngx_string("proxy_temp_path"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
      ngx_conf_set_path_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),
      NULL  
    },
...
}
 
读程序就是读程序架构,读数据处理流程。下面可以分专题进行研究。
1. 配置文件的读取。
在 ngx_conf_parse(&conf, &cycle->conf_file) 读取到每一个属性或是命令, 解析和执行命令
cycle->conf_file 自然是nginx.conf,  
在nginx.c 902 行付过值        ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);
conf 是输出。
 
ngx_conf_file.c line:674                 word = ngx_array_push(cf->args);  
word 是一个 ngx_str_t *,
 
line:679                 word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
word->data, 另外分配内存。将来把读来的token, 存到word->data(void *), 长度存入word->len(int)
 
ngx_array_push 显然是分配了一个ngx_str_t 内存给word, 但又与cf->args 是什么关系呢?
cf->args 是一个 ngx_array_t *.
ngx 数组是如下的一个结构:
typedef struct {
    void        *elts;     // 元素指针
    ngx_uint_t   nelts;     //目前的元素个数
    size_t       size;     // 本数组元素的大小
    ngx_uint_t   nalloc; // 本数组能包含的元素个数
    ngx_pool_t  *pool;  // 内存池
} ngx_array_t;  
 
当数组没有满时,只要返回数组的一个插槽位置即可。(参加 ngx_array.c)
elt = (u_char *) a->elts + a->size * a->nelts;
a->nelts++;
return elt;
如果池子已满,则扩大一倍分配内存,并把旧的内容copy 到新分配的内存中。再继续...,
可见,ngx 数组是一个可扩展的连续的内存块, 这个数组变量管理了这块内存。
这个结构,存储了每行的tokens.
 
2.配置信息的使用。
读到了信息当然要使用,首先是在各个模块中暴力查找命令字。 ngx 做了些优化,使查找变得快一点。
就是采用ngx 字符串结构,直接比较长度,不等查下一个。长度相等了,再比较字符串。
找到了命令,再回调用户定义的set 命令,存储到用户的配置信息。
 rv = cmd->set(cf, cmd, conf);
cf 保存了配置文件读取的信息,cmd 为查到的命令, conf 是用户ctx信息,是数据将要储存的位置。
在初始化的时候返回给系统的。
以core 模块的 worker_processes 为例:
worker_processes 4
 
在nginx.c line:1361        ccf->worker_processes = ngx_atoi(value[1].data, value[1].len);
把整数4 存到了conf->worker_processes 偏移处。
配置文件中的worker_processes 是字符串,要与cmd->name比较,而conf->worker_processes 是一个偏移地址。
两者没有关联。
 
通过以上复杂的过程,说明了配置文件的信息如何传递到用户的module 中去。
 
0 0
原创粉丝点击