Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379968
  • 博文数量: 56
  • 博客积分: 1449
  • 博客等级: 中尉
  • 技术积分: 822
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-08 10:24
文章分类

全部博文(56)

文章存档

2014年(7)

2012年(13)

2011年(10)

2010年(26)

分类: 服务器与存储

2014-08-13 14:11:02

nginx采用事件驱动型模型来使整个系统运作. 事件模块相当于nginx的引擎. 负责侦听事件源, 收集分发事件.
下面分析核心事件模块的代码.

首先来看处于nginx核心的模块ngx_events_module.

点击(此处)折叠或打开

  1. ngx_module_t ngx_events_module = {
  2.     NGX_MODULE_V1,
  3.     &ngx_events_module_ctx, /* module context */
  4.     ngx_events_commands, /* module directives */
  5.     NGX_CORE_MODULE, /* module type */
  6.     NULL, /* init master */
  7.     NULL, /* init module */
  8.     NULL, /* init process */
  9.     NULL, /* init thread */
  10.     NULL, /* exit thread */
  11.     NULL, /* exit process */
  12.     NULL, /* exit master */
  13.     NGX_MODULE_V1_PADDING
  14. };
ngx_events_module_ctx类型为ngx_core_module_t. 这事核心模块定义的一个接口. 提供创建配置上下文结构体以及初始化配置上下文结构体的方法.

点击(此处)折叠或打开

  1. static ngx_core_module_t ngx_events_module_ctx = {
  2.     ngx_string("events"),
  3.     NULL,
  4.     ngx_event_init_conf
  5. };
ngx_event_module创建配置上下文结构体的方法为空. 后面会看到事件模块配置上下文的创建.

点击(此处)折叠或打开

  1. static char *
  2. ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
  3. {
  4.     if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
  5.         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  6.                       "no \"events\" section in configuration");
  7.         return NGX_CONF_ERROR;
  8.     }

  9.     return NGX_CONF_OK;
  10. }
ngx_events_module初始化配置上下文检查配置上下文结构体是否已创建. 若没有创建, 则说明在配置文件中没有发现"events"配置块.
事件模块是nginx的引擎. 没有定义"events"配置块. nginx就不能运行.

ngx_events_commands类型为ngx_command_t[], 每一项说明该模块支持的配置指令, 以及如何处理该指令的方法.

点击(此处)折叠或打开

  1. static ngx_command_t ngx_events_commands[] = {

  2.     { ngx_string("events"),
  3.       NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
  4.       ngx_events_block,
  5.       0,
  6.       0,
  7.       NULL },

  8.       ngx_null_command
  9. };
ngx_events_module只支持"events"指令, 该指令出现在主配置环境, 是一个配置块, 并且不带任何参数.
当在ngx_init_cycle中调用ngx_conf_parse解析配置文件时, 在配置文件中发现"events"配置块就会调用ngx_events_block方法.

下面来分析ngx_events_block函数:

点击(此处)折叠或打开

  1. if (*(void **) conf) {
  2.         return "is duplicate";
  3.     }
这里的conf即为&cycle->conf_ctx[ngx_events_module.index] 是ngx_events_module的配置上下文指针的地址,  说明一个配置文件中
只能有一个"events"配置块.

点击(此处)折叠或打开

  1. /* count the number of the event modules and set up their indices */

  2.     ngx_event_max_module = 0;
  3.     for (i = 0; ngx_modules[i]; i++) {
  4.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
  5.             continue;
  6.         }

  7.         ngx_modules[i]->ctx_index = ngx_event_max_module++;
  8.     }
为所有属于NGX_EVENT_MODULE的模块分配索引, 其用来索引每个事件模块的配置上下文结构体指针在核心事件模块配置上下文指针数组的位置.

点击(此处)折叠或打开

  1. ctx = ngx_pcalloc(cf->pool, sizeof(void *));
  2.     if (ctx == NULL) {
  3.         return NGX_CONF_ERROR;
  4.     }

  5.     *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
  6.     if (*ctx == NULL) {
  7.         return NGX_CONF_ERROR;
  8.     }

  9.     *(void **) conf = ctx;
分配一个二维数组, 相当于ctx[1][ngx_event_max_module], 这个二维指针数组即使核心事件模块ngx_events_module的配置上下. 每个指针指向事件模块
的配置上下结构体. 其实可以直接分配一个一维的指针数组. 难道是为了强行满足ngx_cycle_t中void ****conf_ctx这个声明么?
前面提到ngx_events_module的创建配置上下文方法为空. 在发现"events"配置块之后在这里进行创建.
接下来的代码遵循模块配置上下文的一贯套路. 创建模块配置上下文, 解析配置文件, 初始化配置上下文.
具体过程见nginx主框架和各个模块交互方式

以上就是关于ngx_events_module的分析, 下面来看同样处于核心地位的事件模块ngx_event_core_module
为避免重复就不贴ngx_event_core_module的定义了.

点击(此处)折叠或打开

  1. ngx_event_module_t ngx_event_core_module_ctx = {
  2.     &event_core_name,
  3.     ngx_event_core_create_conf, /* create configuration */
  4.     ngx_event_core_init_conf, /* init configuration */

  5.     { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
  6. };
这是核心事件模块为事件模块定义的接口, 方法包括创建配置上下文, 初始化配置上下文, 以及处理事件的各种方法, 如加入删除某事件.
这个事件模块也处于核心地位, 故不提供具体的事件处理方法.
ngx_event_core_create_conf创建配置上下文结构体ngx_event_conf_t.

下面来看ngx_event_conf_t的初始化 ngx_event_core_init_conf. 以默认使用的epoll事件模块为例.

点击(此处)折叠或打开

  1. fd = epoll_create(100);

  2.     if (fd != -1) {
  3.         (void) close(fd);
  4.         module = &ngx_epoll_module;

  5.     } else if (ngx_errno != NGX_ENOSYS) {
  6.         module = &ngx_epoll_module;
  7.     }
先调用epoll_create创建epoll实例, 若成功或者错误代码不是函数未实现, 则确定使用ngx_epoll_module.

点击(此处)折叠或打开

  1. ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
  2.     cycle->connection_n = ecf->connections;

  3.     ngx_conf_init_uint_value(ecf->use, module->ctx_index);

  4.     event_module = module->ctx;
  5.     ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

  6.     ngx_conf_init_value(ecf->multi_accept, 0);
  7.     ngx_conf_init_value(ecf->accept_mutex, 1);
  8.     ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
初始化ngx_event_conf_t中的一些字段. ecf->connections指明连接池的大小.
ecf->use指明所使用的事件模块的配置上下文索引. 用它可以确定使用哪个事件模块.
ecf->name指向所使用模块的名称字符串.
ecf->multi_accept默认为0, 表示一次只accept一个连接.
ecf->accept_mutex默认值为1, 表明使用负载均衡锁.

ngx_event_core_module支持的配置指令在ngx_event_core_commands指明.
包括worker_connections, use, multi_accept, accept_mutex等.

以上代码都是关于配置上下文的创建, 解析以及初始化, 确定真正所使用的事件模块, 这个事件模块是引擎的核心部分. 即epoll.

下面来看ngx_event_core_module实现的init_module方法: ngx_event_module_init

点击(此处)折叠或打开

  1. /* cl should be equal to or greater than cache line size */

  2.     cl = 128;

  3.     size = cl /* ngx_accept_mutex */
  4.            + cl /* ngx_connection_counter */
  5.            + cl; /* ngx_temp_number */
确定分配共享存储区的大小. 正如注释所示: 第一个128字节用于ngx_accept_mutex, 第二个128字节用于ngx_connection_counter,
第三个128字节用于ngx_temp_number.

点击(此处)折叠或打开

  1.     if (ngx_shm_alloc(&shm) != NGX_OK) {
  2.         return NGX_ERROR;
  3.     }

  4.     shared = shm.addr;

  5.     ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
  6.     ngx_accept_mutex.spin = (ngx_uint_t) -1;

  7.     if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
  8.                          cycle->lock_file.data)
  9.         != NGX_OK)
  10.     {
  11.         return NGX_ERROR;
  12.     }

  13.     ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
  14.     ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
ngx_shm_alloc(&shm)在linux中用mmap来实现, shm.addr即为返回的共享存储区的地址.
红底标识的语句即为共享存储区的使用.
ngx_shmtx_create初始化ngx_accept_mutex.lock = shared. 所有进程通过这个锁来同步.
因为shared在共享存储区. 所以对它的修改所有进程可见.

可见ngx_event_module_init所做的就是创建共享存储区用于保存互斥锁 以及一些全局统计量.

下面再来看ngx_event_core_module的init_process方法: ngx_event_process_init

点击(此处)折叠或打开

  1. if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
  2.         ngx_use_accept_mutex = 1;
  3.         ngx_accept_mutex_held = 0;
  4.         ngx_accept_mutex_delay = ecf->accept_mutex_delay;

  5.     } else {
  6.         ngx_use_accept_mutex = 0;
  7.     }
当nginx以master方式(默认为master), work进程数大于1(默认配置为1), 并且使用接收互斥锁(默认使用)时,
确定使用互斥锁来进行负载均衡互斥锁. 至于如何实现暂时不用管.

点击(此处)折叠或打开

  1. if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
  2.         return NGX_ERROR;
  3.     }
初始化保存定时器的红黑树.

点击(此处)折叠或打开

  1. for (m = 0; ngx_modules[m]; m++) {
  2.         if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
  3.             continue;
  4.         }

  5.         if (ngx_modules[m]->ctx_index != ecf->use) {
  6.             continue;
  7.         }

  8.         module = ngx_modules[m]->ctx;

  9.         if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
  10.             /* fatal */
  11.             exit(2);
  12.         }

  13.         break;
  14.     }
找到指定使用的模块, 这个指定的模块保存于ecf->use. 在上面讲ngx_event_core_init_conf有提到.
找到该模块后. 调用这个模块的action.init方法.
ngx_timer_resolution默认为0, 所以先略过那个if语句.

点击(此处)折叠或打开

  1. cycle->connections =
  2.         ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
分配cycle->connection_n个ngx_connection_t结构作为连接池. cycle->connection_n即为event conf结构中connection. 默认为512.

点击(此处)折叠或打开

  1. cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
  2.                                    cycle->log);
  3. cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                        cycle->log);
分配同连接池一样大小的读事件和写事件池.

点击(此处)折叠或打开

  1. i = cycle->connection_n;
  2.     next = NULL;

  3.     do {
  4.         i--;

  5.         c[i].data = next;
  6.         c[i].read = &cycle->read_events[i];
  7.         c[i].write = &cycle->write_events[i];
  8.         c[i].fd = (ngx_socket_t) -1;

  9.         next = &c[i];

  10. #if (NGX_THREADS)
  11.         c[i].lock = 0;
  12. #endif
  13.     } while (i);
将用连接结构体中data字段作为链表的next指针, 将连接池以链表的形式组织起来. 并且为连接池里的每个
连接分配一个读事件和一个写事件结构. 连接结构的索引位置同读写事件结构在数组中的索引位置相同.

点击(此处)折叠或打开

  1. cycle->free_connections = next;
  2.     cycle->free_connection_n = cycle->connection_n;
保存空闲连接池链表表头和空闲连接大小.

点击(此处)折叠或打开

  1. ls = cycle->listening.elts;
  2.     for (i = 0; i < cycle->listening.nelts; i++) {

  3.         c = ngx_get_connection(ls[i].fd, cycle->log);
  4. c->listening = &ls[i];
  5.         ls[i].connection = c;
  6.         rev->handler = ngx_event_accept;

  7.             if (ngx_use_accept_mutex) {
  8.                 continue;
  9.             }

  10.             if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
  11.                 return NGX_ERROR;
  12.             }
  13.         }
为每个监听结构分配一个连接.  设置读事件的处理方法为ngx_event_accept, 并且将读事件加入epoll中.
这就相当于在事件驱动模型中添加事件源. 当关注的事件发生时会自动调用其指定的回调方法.

简单来讲ngx_event_core_module的init_process方法所做的就是:
1.调用所使用的事件模块引擎的action.init方法来让该引擎初始化自己的核心部件, 等待被使用.
2.分配连接池, 读事件池, 写事件池, 并且每个连接分配一个读事件和一个写事件
3.为每个注册的监听结构分配连接结构. 初始化其读事件处理方法. 像事件引擎注册该事件源.
阅读(7525) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~