nginx 源码学习笔记(十四)—— 全局变量ngx_cycle

再打算正式开始研究core模块式,发现有一个很重要的变量ngx_cycle_t,一直伴随,如果不懂ngx_cycle可能读起代码来回非常困难,这里就来详细学习一下吧。本文大部分灵感来自于。http://blog.csdn.net/livelylittlefish/article/details/7247080 和http://blog.sina.com.cn/s/blog_677be95b0100iivi.html 谢谢作者提供很详细的资料。

 

依照惯例我们直接来看下

  1. <span style="font-size:16px;">/src/core/ngx_cycle.h  
  2. struct ngx_cycle_s {  
  3.     void                  ****conf_ctx;  //配置上下文数组(含所有模块)  
  4.     ngx_pool_t               *pool;      //内存池  
  5.   
  6.     ngx_log_t                *log;       //日志  
  7.     ngx_log_t                 new_log;  
  8.   
  9.     ngx_connection_t        **files;     //连接文件  
  10.     ngx_connection_t         *free_connections;  //空闲连接  
  11.     ngx_uint_t                free_connection_n; //空闲连接个数  
  12.   
  13.     ngx_queue_t               reusable_connections_queue;  //再利用连接队列  
  14.   
  15.     ngx_array_t               listening;     //监听数组  
  16.     ngx_array_t               pathes;        //路径数组  
  17.     ngx_list_t                open_files;    //打开文件链表  
  18.     ngx_list_t                shared_memory; //共享内存链表  
  19.   
  20.     ngx_uint_t                connection_n;  //连接个数  
  21.     ngx_uint_t                files_n;       //打开文件个数  
  22.   
  23.     ngx_connection_t         *connections;   //连接  
  24.     ngx_event_t              *read_events;   //读事件  
  25.     ngx_event_t              *write_events;  //写事件  
  26.   
  27.     ngx_cycle_t              *old_cycle;     //old cycle指针  
  28.   
  29.     ngx_str_t                 conf_file;     //配置文件  
  30.     ngx_str_t                 conf_param;    //配置参数  
  31.     ngx_str_t                 conf_prefix;   //配置前缀  
  32.     ngx_str_t                 prefix;        //前缀  
  33.     ngx_str_t                 lock_file;     //锁文件  
  34.     ngx_str_t                 hostname;      //主机名  
  35. };</span>  


 

该结构体的大小是确定的,sizeof(ngx_cycle_t)=224

其中,

  • pathes数组元素结构为ngx_path_t
  • open_files链表元素结构为ngx_open_file_t
  • shared_memory链表元素结构为ngx_shm_zone_t
  • listening数组元素结构为ngx_listening_t,该数组用来存放监听套接字。

 

 

再来看下ngx_init_cycle函数的处理过程

1.调用ngx_timezone_update()、ngx_timeofday()和ngx_time_update(0, 0)做时间校准

 

2.创建一个新的ngx_cycle_t变量cycle,并且初始化其大部分的成员字段,有一些是从传入的old_cycle直接拷贝过来的,这些字段包括:log,conf_prefix,prefix,conf_file,conf_param

  1. <span style="font-size:16px;">src/core/ngx_cycle.c  
  2.   
  3. cycle->pool = pool;  
  4. cycle->log = log;  
  5. cycle->new_log.log_level = NGX_LOG_ERR;  
  6. cycle->old_cycle = old_cycle;  
  7.   
  8. cycle->conf_prefix.len = old_cycle->conf_prefix.len;  
  9. cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);  
  10.   
  11. cycle->prefix.len = old_cycle->prefix.len;  
  12. cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);  
  13.   
  14. cycle->conf_file.len = old_cycle->conf_file.len;  
  15. cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);  
  16.   
  17. ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,  
  18.             old_cycle->conf_file.len + 1);  
  19.   
  20. cycle->conf_param.len = old_cycle->conf_param.len;  
  21. cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);</span>  


 

还有一些字段会判断一下old_cycle中是否存在,如果存在,则取得这些字段的占用空间,在cycle中申请等大的空间,并初始化(不拷贝),否则就申请默认大小的空间,这些字段有:pathes,open_files,share_memory,listening

  1. <span style="font-size:16px;">src/core/ngx_cycle.c  
  2.   
  3. n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;  
  4.   
  5. cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));  
  6. cycle->pathes.nelts = 0;  
  7. cycle->pathes.size = sizeof(ngx_path_t *);  
  8. cycle->pathes.nalloc = n;  
  9. cycle->pathes.pool = pool;  
  10.   
  11.   
  12. if (old_cycle->open_files.part.nelts) {  
  13.     n = old_cycle->open_files.part.nelts;  
  14.     for (part = old_cycle->open_files.part.next; part; part = part->next) {  
  15.         n += part->nelts;  
  16.     }  
  17.   
  18. else {  
  19.     n = 20;  
  20. }  
  21.   
  22. if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))  
  23.     != NGX_OK)  
  24. {  
  25.     ngx_destroy_pool(pool);  
  26.     return NULL;  
  27. }  
  28.   
  29.   
  30. if (old_cycle->shared_memory.part.nelts) {  
  31.     n = old_cycle->shared_memory.part.nelts;  
  32.     for (part = old_cycle->shared_memory.part.next; part; part = part->next)  
  33.     {  
  34.         n += part->nelts;  
  35.     }  
  36.   
  37. else {  
  38.     n = 1;  
  39. }  
  40.   
  41. if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))  
  42.     != NGX_OK)  
  43. {  
  44.     ngx_destroy_pool(pool);  
  45.     return NULL;  
  46. }  
  47.   
  48. n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;  
  49. cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));  
  50. cycle->listening.nelts = 0;  
  51. cycle->listening.size = sizeof(ngx_listening_t);  
  52. cycle->listening.nalloc = n;  
  53. cycle->listening.pool = pool;</span>  


 

 

还有一些字段是重新创建或者第一次赋值的:pool,new_log.log_level(=NGX_LOG_ERR),old_cycle(=old_cycle),hostname(gethostname);


最重要的一个字段是conf_ctx,它被初始化为ngx_max_module个void *指针,这预示着conf_ctx是所有模块的配置结构的指针数组;

  1. <span style="font-size:16px;">for (i = 0; ngx_modules[i]; i++) {  
  2.     if (ngx_modules[i]->type != NGX_CORE_MODULE) {  
  3.         continue;  
  4.     }  
  5.   
  6.     module = ngx_modules[i]->ctx;  
  7.   
  8.     if (module->create_conf) {  
  9.         rv = module->create_conf(cycle);//<span style="font-family:Verdana;">调用所有核心类模块的钩子create_conf</span>  
  10.         if (rv == NULL) {  
  11.             ngx_destroy_pool(pool);  
  12.             return NULL;  
  13.         }  
  14.         cycle->conf_ctx[ngx_modules[i]->index] = rv;//<span style="font-family:Verdana;">返回的配置结构指针放到conf_ctx数组中,偏移位置为ngx_module_t.index</span>  
  15.     }  
  16. }</span>  


 

3.从命令行和配置文件中把所有配置更新到cycle的conf_ctx中:

首先调用ngx_conf_param把命令行中的指令(-g directives)转换为配置结构并把指针加入到cycle.conf_ctx中;

接着调用ngx_conf_parse(..,filename)把配置文件中的指令转换为配置结构并把指针加入到cycle.conf_ctx中。

  1. <span style="font-size:16px;">conf.ctx = cycle->conf_ctx;  
  2. conf.cycle = cycle;  
  3. conf.pool = pool;  
  4. conf.log = log;  
  5. conf.module_type = NGX_CORE_MODULE;  
  6. conf.cmd_type = NGX_MAIN_CONF;  
  7.       
  8. if (ngx_conf_param(&conf) != NGX_CONF_OK) {  
  9.     environ = senv;  
  10.     ngx_destroy_cycle_pools(&conf);  
  11.     return NULL;  
  12. }  
  13.   
  14. if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {  
  15.     environ = senv;  
  16.     ngx_destroy_cycle_pools(&conf);  
  17.     return NULL;  
  18. }</span>  


 

ngx_conf_param最后也会调用ngx_conf_parse(..,NULL),所以配置的更新主要是在ngx_conf_parse中进行的,这个函数中有一个for循环,每次循环调用ngx_conf_read_token取得一个配置指令(以;结尾)(这里在上一节已经详细将结果),然后调用ngx_conf_handler处理这条指令,ngx_conf_handler每次都会遍历所有模块的指令集,查找这条配置指令并分析其合法性,如果指令正确,则会创建配置结构并把指针加入到cycle.conf_ctx中,配置结构的赋值是调用该指令的钩子set完成的。

遍历指令集的过程首先是遍历所有的核心类模块,若是event类的指令,则会遍历到ngx_events_module,这个模块是属于核心类的,其钩子set又会嵌套调用ngx_conf_parse去遍历所有的event类模块,同样的,若是http类指令,则会遍历到ngx_http_module,该模块的钩子set进一步遍历所有的http类模块,mail类指令会遍历到ngx_mail_module,该模块的钩子进一步遍历到所有的mail类模块。要特别注意的是:这三个遍历过程中会在适当的时机调用event类模块、http类模块和mail类模块的创建配置和初始化配置的钩子。从这里可以看出,event、http、mail三类模块的钩子是配置中的指令驱动的;
4.调用所有核心类模块的钩子init_conf,把模块的配置结构作为一个参数传入:init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index);

  1. <span style="font-size:16px;">src/core/ngx_cycle.c  
  2.   
  3. for (i = 0; ngx_modules[i]; i++) {  
  4.     if (ngx_modules[i]->type != NGX_CORE_MODULE) {  
  5.         continue;  
  6.     }  
  7.   
  8.     module = ngx_modules[i]->ctx;  
  9.   
  10.     if (module->init_conf) {  
  11.         if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])  
  12.             == NGX_CONF_ERROR)  
  13.         {  
  14.             environ = senv;  
  15.             ngx_destroy_cycle_pools(&conf);  
  16.             return NULL;  
  17.         }  
  18.     }  
  19. }</span>  


 

5.获得核心模块ngx_core_dodule的配置结构,然后调用ngx_create_pidfile创建pid文件。获取配置结构的代码:ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module),这里的ngx_get_conf是一个宏定义:#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index];

  1. <span style="font-size:16px;">ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  2.   
  3.     if (ngx_test_config) {  
  4.   
  5.         if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {  
  6.             goto failed;  
  7.         }  
  8.   
  9.     } else if (!ngx_is_init_cycle(old_cycle)) {  
  10.   
  11.         /* 
  12.          * we do not create the pid file in the first ngx_init_cycle() call 
  13.          * because we need to write the demonized process pid 
  14.          */  
  15.   
  16.         old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,  
  17.                                                    ngx_core_module);  
  18.         if (ccf->pid.len != old_ccf->pid.len  
  19.             || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)  
  20.         {  
  21.             /* new pid file name */  
  22.   
  23.             if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {  
  24.                 goto failed;  
  25.             }  
  26.   
  27.             ngx_delete_pidfile(old_cycle);  
  28.         }  
  29.     }</span>  


 

6.调用ngx_test_lockfile(filename,log),ngx_create_pathes(cycle,user),接着打开errorlog文件并赋值给cycle->new_log.file:cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);

 

7.打开新文件,在第2步的时候提到cycle->open_files这个链表是空的,只是给它预先分配了空间,并没有数据,这里之所以可能会有文件被打开,估计是前面读配置文件的时候,调用各个钩子的过程中,填充了这个链表,把ngx_open_file_t结构变量填充进来(结构体中包含要打开文件的路径信息),这是我猜测的,之后再验证:)接着修改一下cycle的成员:cycle->log = &cycle->new_log;pool->log = &cycle->new_log;

 

8.创建共享内存,和open_files类似,在第2步的时候cycle->share_memory也初始化为一个空的链表,也是预分配了空间,如果此时链表中已经被填充了ngx_shm_zone_t结构变量(其中包含需要共享内存的尺寸和标识等信息),那么这里就会分配共享内存,并且调用合适的初始化钩子初始化分配的共享内存,每块共享内存都会有name标识,这里也会做一些排重,已经分配的就不会再去分配,从对open_files和share_memory的处理过程可以看出,nginx在资源管理上是集中分配的,请求资源的时候分配说明性的结构变量,然后在恰当的时机才去真正分配资源;

 

9.处理listening sockets,cycle->listening是ngx_listening_t结构的数组,把cycle->listening于old_cycle->listening进行比较,设置cycle->listening的一些状态信息,接着调用ngx_open_listening_sockets(cycle)启动cycle->listening中的所有监听socket,循环调用socket,bind,listen完成服务端监听socket的启动。接着调用ngx_configure_listening_sockets(cycle)配置监听socket,会根据ngx_listening_t中的状态信息设置socket的读写缓存和TCP_DEFER_ACCEPT;

 

10.调用所有模块的钩子init_module;

  1. <span style="font-size:16px;">for (i = 0; ngx_modules[i]; i++) {  
  2.         if (ngx_modules[i]->init_module) {  
  3.             if (ngx_modules[i]->init_module(cycle) != NGX_OK) {  
  4.                 /* fatal */  
  5.                 exit(1);  
  6.             }  
  7.         }  
  8.     }</span>  


 

11.关闭或者删除一些残留在old_cycle中的资源,首先释放不用的共享内存,接着关闭不使用的监听socket,再关闭不使用的打开文件,最后把old_cycle放入ngx_old_cycles中,这是一个ngx_cycle_t *的数组,最后设定一个定时器,定期回调ngx_cleaner_event清理ngx_old_cycles,这里设置了30000ms清理一次。

  1. <span style="font-size:16px;">if (!ngx_cleaner_event.timer_set) {  
  2.         ngx_add_timer(&ngx_cleaner_event, 30000);  
  3.         ngx_cleaner_event.timer_set = 1;  
  4.     }</span>  


 

 放上一张图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值