Daly的游戏人生

Nginx源码学习(2) ---- 模块化及配置

    nginx的大部分功能都采用模块化的方式加载到主程序,通过配置文件实现模块的各种加载和参数配置。模块化架构分析是nginx最关键的部分之一,本文作为参考文献[1]的补充,深入分析nginx如何把配置文件和模块管理联系到一起的。
    可以说整个nginx都是由模块来组成。nginx的模块可以分为四种:core、 event、http和mail, core是核心模块,event是事件处理模块,本文重点以这两个模块为例进行分析。在编译配置的时候,由自动脚本生成的objs/ngx_modules.c,定义了要包含的哪些模块,保存到全局变量ngx_modules[] 中
   
ngx_module_t *ngx_modules[] = {
    
&ngx_core_module,
    
&ngx_errlog_module,
    
&ngx_conf_module,
    
&ngx_events_module,
    
&ngx_event_core_module,
    
&ngx_epoll_module,
    
&ngx_http_module,
    
//.省略
    NULL
}

  而ngx_core_module, ngx_events_module这些全局变量,则在模块的实现文件中定义。ngx_module_t的结构的主要成员见下图。
 

commands是ngx_command_t的数组,表示该模块支持的配置命令。
ctx是模块上下文,保存着该模块的关键的信息,比如event模块上下文为ngx_event_module_t结构,该结构有一个ngx_event_actions_t成员,保存着事件处理函数,它把事件处理抽象成几个函数(添加/删除事件,查询事件,事件响应),具体的epoll, select, kqueue等API根据配置来填入,下文将详细讲述。 模块上下文一般都有create_conf和init_conf两个钩子,由于每个模块都有一个属于自己的配置结构体,用来保存该模块的配置参数,这两个函数钩子就是创建和初始化该模块的配置结构体。

nginx的进程启动过程可参考文献[1],下面就一步步分析从配置文件到模块载入和配置的整个过程。

nginx的配置文件示例如下图

#user  nobody;

worker_processes  
10;
error_log  
/home/weblogs/error.log crit;
#error_log  logs/error.log  notice;

pid        logs
/nginx.pid;
worker_rlimit_nofile 
51200;

events {
    
use epoll;
    worker_connections  
51200;
}
http {
  

  server {
     server_name  xxx;
     
listen     80;
  }
  
.
}

可以看到,nginx配置文件格式是:
   配置节名(也是命令的一种) {
       下一级配置节名{
           。。。 。。。
       }
       命令  值
   }

这里的命令就是上文的ngx_command_t结构中的name字符串,最外层的是对应ngx_core_module中的命令。整个配置过程如下:

main()
{
   ....
    // 所有模块点一下数, 初始化index
    ngx_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

   ngx_init_cycle(); //初始化并设置全局变量
   ///..........
   ngx_master_process_cycle(); //启动进程,执行主循环干活, 参见文档[2]
   ///..........
}

//设置全局变量
ngx_init_cycle()
{
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }
        module = ngx_modules[i]->ctx;    //取得模块上下文, 例如ngx_core_module中的ngx_core_module_ctx

       
// 调度core类型模块的钩子create_conf,并且把创建的配置结构体变量存放到cycle->conf_ctx中
        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[ngx_modules[i]->index] = rv;   //这里是配置信息的结构体
        }
   
}
    /* 例如ngx_core_module模块,create_conf钩子是调用ngx_core_module_create_conf, 该函数创建一个ngx_core_conf_t结构体,保存了全局的配置信息,比如子进程数,连接数,文件路径等顶层信息,并返回。之后保存在全局cycle的conf_ctx对应位置(对应于模块index)。 */
    //....
    ngx_conf_parse(&conf, &cycle->conf_file);   //分析配置文件


    // 调度core模块的钩子init_conf,设置刚才创建的配置结构体变量(用从配置文件中读取的数据赋值)
    init_conf根据之前的conf_parse结果,填充配置结构体数值


    // 调度所有模块的init_module钩子,初始化模块
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_module) {   //调用模块的初始化函数
            if (ngx_modules[i]->init_module(cycle) != NGX_OK) {

                exit(1);
            }
        }
    }

   .....
}

ngx_conf_parse()
{
   //循环对配置文件进行语法分析
   ngx_conf_read_token(); //读取下一个token
   //根据{ }来设置ngx_conf_s中的变量,改变当前的模块配置节
   ngx_conf_handler();   //分析该配置节下的模块命令.
}

ngx_conf_handler()
{
   //1. 遍历模块的command_s数组,strcmp来比对命令
   //2. 根据command_t的参数指定,读取后续参数
   //3. 获取该模块的配置结构体,以及该命令相关的参数在配置结构体中的偏移量(根据command_t中的offset)
   //4. 调用command_t中的set钩子,把读到参数值赋值给配置结构体
}

最后在模块的init_conf函数中可以根据配置值,来做相应的操作。
比如底层的事件处理库,use指令指示用select还是epoll呢,比如use epoll
那么这些event模块在init的时候,就会判断配置结构体中的use, 如果是自己,则把event_actions赋值给全局变量ngx_event_actions,
这样,其他模块就可以通过ngx_event_actions中的钩子进行事件处理操作, 隔离了底层实现。

参考文献
[1] nginx源码分析--模块化(1) http://blog.sina.com.cn/s/blog_677be95b0100iive.html
[2] nginx的进程模型。http://simohayha.javaeye.com/blog/467940

posted on 2010-06-18 16:15 Daly 阅读(2464) 评论(0)  编辑 收藏 引用 所属分类: C/C++网络编程


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理