您好,欢迎访问一九零五行业门户网

nginx 源码学习笔记(二十)—— event 模块一 ——初始化

读完之前的学习笔记,相信已经对nginx的启动流程有了一定的认识,从这一节起我们想深入各个模块,学习各个模块的内的主要操作。
本文来自于:http://blog.csdn.net/lengzijian/article/details/7598996
今天我们就来学习下event模块,在之前的启动里多次提到了调用各个模块的钩子函数,我们先来回忆一下关于event模块钩子函数的执行,也是event模块启动的步骤:
1.创建conf(creat_conf):
ngx_event_create_conf()
该方法,主要是创建了一个ngx_event_conf_t结构体,并且分配内存空间。
2.读取配置文件:
例如读取到的文件有如下行:
[cpp] view plaincopyprint?
events   {    use epoll;    worker_connections 10240;  }  
这个地方的events是一个block指令,在大括号内可以配置很多指令,这些指令定义在src/event/ngx_event.c中
[cpp] view plaincopyprint?
static ngx_command_t  ngx_event_core_commands[] = {        { ngx_string(worker_connections),        ngx_event_conf|ngx_conf_take1,        ngx_event_connections,        0,        0,        null },                  ...(此处省略)        { ngx_string(connections),        ngx_event_conf|ngx_conf_take1,        ngx_event_connections,        0,        0,        null },        { ngx_string(use),        ngx_event_conf|ngx_conf_take1,        ngx_event_use,        0,        0,        null },        ngx_null_command  };  
当解析到events是会回调如下函数:
[cpp] view plaincopyprint?
src/event/ngx_event.c  static char *  ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  {      char                 *rv;      void               ***ctx;      ngx_uint_t            i;      ngx_conf_t            pcf;      ngx_event_module_t   *m;        /* count the number of the event modules and set up their indices */      //计算event模块数量,并且记录      ngx_event_max_module = 0;      for (i = 0; ngx_modules[i]; i++) {          if (ngx_modules[i]->type != ngx_event_module) {              continue;          }            ngx_modules[i]->ctx_index = ngx_event_max_module++;      }        ctx = ngx_pcalloc(cf->pool, sizeof(void *));      if (ctx == null) {          return ngx_conf_error;      }      //为每一个event模块分配空间,用来保存响应配置结构的地址      //共分配了ngx_event_max_module个空间      *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));      if (*ctx == null) {          return ngx_conf_error;      }        *(void **) conf = ctx;        for (i = 0; ngx_modules[i]; i++) {          if (ngx_modules[i]->type != ngx_event_module) {              continue;          }            m = ngx_modules[i]->ctx;          //循环调用每个模块的creat_conf钩子函数,用于创建配置结构          if (m->create_conf) {              (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);              if ((*ctx)[ngx_modules[i]->ctx_index] == null) {                  return ngx_conf_error;              }          }      }        pcf = *cf;      cf->ctx = ctx;      cf->module_type = ngx_event_module;      cf->cmd_type = ngx_event_conf;      //由于events是一个block指令,events域下还可以配置很多其他指令,      //比如之前提过的use等,现在开始解析events block中的指令,完成初始化工作。      rv = ngx_conf_parse(cf, null);        *cf = pcf;        if (rv != ngx_conf_ok)          return rv;            for (i = 0; ngx_modules[i]; i++) {          if (ngx_modules[i]->type != ngx_event_module) {              continue;          }          m = ngx_modules[i]->ctx;          //循环执行每个event模块的init_conf函数,初始化配置结构          if (m->init_conf) {              rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);              if (rv != ngx_conf_ok) {                  return rv;              }          }      }        return ngx_conf_ok;  }  
ngx_events_block()函数中最重要的一个过程就是调用ngx_conf_parse(cf, null),此处调用ngx_conf_parse()的作用就是完成配置文件中events{}这个block的解析,从而调用其下所有的配置指令的回调函数,完成解析配置文件的初始化工作。但是这里我个人有个问题,待问完前辈之后,在指明问题和答案******。
2.初始化conf(init_conf)
ngx_event_init_conf()
该方法,主要是初始化ngx_event_conf_t结构体。
3.ngx_event_module_init
从名字上看是模块的初始化操作,但是纵观各个模块源代码,发现很多模块都没有init回调函数。这里本人也在纠结为什么,希望在学完全部代码后,能够找到答案。
[cpp] view plaincopyprint?
src/event/ngx_event.c  static ngx_int_t  ngx_event_module_init(ngx_cycle_t *cycle)  {      void              ***cf;      u_char              *shared;      size_t               size, cl;      ngx_shm_t            shm;      ngx_time_t          *tp;      ngx_core_conf_t     *ccf;      ngx_event_conf_t    *ecf;            //判断ngx_events_module是否调用过初始化conf操作      cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);        if (cf == null) {          ngx_log_error(ngx_log_emerg, cycle->log, 0,                        no \events\ section in configuration);          return ngx_error;      }            //获取ngx_event_core_module模块的配置结构      ecf = (*cf)[ngx_event_core_module.ctx_index];            //查看是否是event中的模块,例如use 。。。。      if (!ngx_test_config && ngx_process         ngx_log_error(ngx_log_notice, cycle->log, 0,                        using the \%s\ event method, ecf->name);      }      //获取ngx_core_module模块的配置结构      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);            //从ngx_core_module模块的配置结构中获取timer_resolution参数      ngx_timer_resolution = ccf->timer_resolution;    #if !(ngx_win32)      {      ngx_int_t      limit;      struct rlimit  rlmt;            //获取当前进程能够打开的最大文件数     man getrlimit      if (getrlimit(rlimit_nofile, &rlmt) == -1) {          ngx_log_error(ngx_log_alert, cycle->log, ngx_errno,                        getrlimit(rlimit_nofile) failed, ignored);        } else {          //如果ngx_event_core_module模块连接数大于当前(软)限制          //并且ngx_core_module最大连接数无限制          //或者ngx_event_core_module连接数大于ngx_core_module最大连接数          if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur              && (ccf->rlimit_nofile == ngx_conf_unset                  || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))          {              limit = (ccf->rlimit_nofile == ngx_conf_unset) ?                           (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;                ngx_log_error(ngx_log_warn, cycle->log, 0,                            %ui worker_connections are more than                             open file resource limit: %i,                            ecf->connections, limit);          }      }      }  #endif /* !(ngx_win32) */        //如果关闭了master进程,就返回      //因为关闭了master进程就是单进程工作方式,      //之后的操作时创建共享内存实现锁等工作,单进程不需要。      if (ccf->master == 0) {          return ngx_ok;      }            //如果已经存在accept互斥体了,不需要再重复创建了      if (ngx_accept_mutex_ptr) {          return ngx_ok;      }          /* cl should be equal or bigger than cache line size */        cl = 128;      //这里创建size大小的共享内存,这块共享内存将被均分成三段      size = cl            /* ngx_accept_mutex */             + cl          /* ngx_connection_counter */             + cl;         /* ngx_temp_number */        //准备共享内存,大小为size,命名nginx_shared_zone,      shm.size = size;      shm.name.len = sizeof(nginx_shared_zone);      shm.name.data = (u_char *) nginx_shared_zone;      shm.log = cycle->log;            //创建共享内存,起始地址保存在shm.addr      if (ngx_shm_alloc(&shm) != ngx_ok) {          return ngx_error;      }      //获取起始地址保存      shared = shm.addr;        //accept互斥体取得共享内存的第一段cl大小内存      ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;      ngx_accept_mutex.spin = (ngx_uint_t) -1;      /*创建accept互斥体          accept互斥体的实现依赖是否支持原子操作,如果有相应的原子操作;     就是用取得的这段共享内存来实现accept互斥体;否则,将使用文件锁     来实现accept互斥体。          accept互斥体的作用是:避免惊群和实现worker进程的负载均衡。          */      if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)          != ngx_ok)      {          return ngx_error;      }            //获取内存的第二段cl大小的地址      ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);        (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);        ngx_log_debug2(ngx_log_debug_event, cycle->log, 0,                     counter: %p, %d,                     ngx_connection_counter, *ngx_connection_counter);      //获取内存的第三段cl大小的地址      ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);        tp = ngx_timeofday();        ngx_random_number = (tp->msec       return ngx_ok;  }  
4.ngx_event_process_init
在之前的worker进程分析中有提到过,当创建了一个worker进程后,worker进程首先就会做进程的初始化工作,此时会调用ngx_event_process_init函数。
[cpp] view plaincopyprint?
src/event/ngx_event.c  static ngx_int_t  ngx_event_process_init(ngx_cycle_t *cycle)  {      ngx_uint_t           m, i;      ngx_event_t         *rev, *wev;      ngx_listening_t     *ls;      ngx_connection_t    *c, *next, *old;      ngx_core_conf_t     *ccf;      ngx_event_conf_t    *ecf;      ngx_event_module_t  *module;            //和之前一样,获取响应模块的配置结构      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);      ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);            //master进程打开,worker进程大于1,已经创建了accept_mutex      //才打开accept互斥体      if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {          ngx_use_accept_mutex = 1; //使用互斥体          ngx_accept_mutex_held = 0; //是否获得accept互斥体          ngx_accept_mutex_delay = ecf->accept_mutex_delay;//争抢互斥体失败后,等待下次争抢时间间隔        } else {          ngx_use_accept_mutex = 0;      }    #if (ngx_threads)      //线程先不讲  #endif      //初始化计数器,此处将会创建一颗红黑树,来维护计时器,之后会详细讲解      if (ngx_event_timer_init(cycle->log) == ngx_error) {          return ngx_error;      }        for (m = 0; ngx_modules[m]; m++) {          //这里之前讲过,跳过非ngx_event_module模块          if (ngx_modules[m]->type != ngx_event_module) {              continue;          }          //非use配置指令指定的模块跳过,linux默认epoll          if (ngx_modules[m]->ctx_index != ecf->use) {              continue;          }            module = ngx_modules[m]->ctx;          /*调用具体时间模块的init函数                  由于nginx实现了很多事件模块,比如:epoll、poll、select、dqueue、aio         (这些模块位于src/event/modules目录中),所以nginx对时间模块进行了一层抽象,         方便了不同的系统使用不同的事件模型,也便于扩展新的时间模型,我们的重点应该         放在epoll上。                  此处的init回调,其实就是调用了ngx_epoll_init函数。module->actions结构封装了         epoll的所有接口函数。nginx就是通过actions结构将epoll注册到事件抽象层中。         actions的类型是ngx_event_action_t,位于src/event/ngx_event.h                  这些具体的内容会在下一节中重点讲解。                  */          if (module->actions.init(cycle, ngx_timer_resolution) != ngx_ok) {              /* fatal */              exit(2);          }            break;      }  //此处省略部分内容      //创建全局的ngx_connection_t数组,保存所有的connection      //由于这个过程是在各个worker进程中执行的,所以每个worker都有自己的connection数组      cycle->connections =          ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);      if (cycle->connections == null) {          return ngx_error;      }        c = cycle->connections;            //创建一个读事件数组      cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,                                     cycle->log);      if (cycle->read_events == null) {          return ngx_error;      }        rev = cycle->read_events;      for (i = 0; i connection_n; i++) {          rev[i].closed = 1;          rev[i].instance = 1;  #if (ngx_threads)          rev[i].lock = &c[i].lock;          rev[i].own_lock = &c[i].lock;  #endif      }      //创建一个写事件数组      cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,                                      cycle->log);      if (cycle->write_events == null) {          return ngx_error;      }        wev = cycle->write_events;      for (i = 0; i connection_n; i++) {          wev[i].closed = 1;  #if (ngx_threads)          wev[i].lock = &c[i].lock;          wev[i].own_lock = &c[i].lock;  #endif      }        i = cycle->connection_n;      next = null;      //初始化整个connection数组      do {          i--;            c[i].data = next;          c[i].read = &cycle->read_events[i];          c[i].write = &cycle->write_events[i];          c[i].fd = (ngx_socket_t) -1;            next = &c[i];    #if (ngx_threads)          c[i].lock = 0;  #endif      } while (i);        cycle->free_connections = next;      cycle->free_connection_n = cycle->connection_n;        /* for each listening socket */      //为每个监听套接字从connection数组中分配一个连接,即一个slot      ls = cycle->listening.elts;      for (i = 0; i listening.nelts; i++) {          //从conneciton中取得一个新的连接solt          c = ngx_get_connection(ls[i].fd, cycle->log);            if (c == null) {              return ngx_error;          }            c->log = &ls[i].log;            c->listening = &ls[i];          ls[i].connection = c;            rev = c->read;            rev->log = c->log;          rev->accept = 1; //读时间发生,调用accept    #if (ngx_have_deferred_accept)          //省略  #endif            if (!(ngx_event_flags & ngx_use_iocp_event)) {              if (ls[i].previous) {                    /*                  * delete the old accept events that were bound to                  * the old cycle read events array                  */                    old = ls[i].previous->connection;                    if (ngx_del_event(old->read, ngx_read_event, ngx_close_event)                      == ngx_error)                  {                      return ngx_error;                  }                    old->fd = (ngx_socket_t) -1;              }          }            //注册监听套接口毒事件的回调函数 ngx_event_accept          rev->handler = ngx_event_accept;                    //使用了accept_mutex,暂时不将监听套接字放入epoll中,而是          //等到worker抢到accept互斥体后,再放入epoll,避免惊群的发生          if (ngx_use_accept_mutex) {              continue;          }                              if (ngx_event_flags & ngx_use_rtsig_event) {              if (ngx_add_conn(c) == ngx_error) {                  return ngx_error;              }            } else {              //没有使用accept互斥体,那么就将此监听套接字放入epoll中。              if (ngx_add_event(rev, ngx_read_event, 0) == ngx_error) {                  return ngx_error;              }          }    #endif        }        return ngx_ok;  }  
到现在为止,事件驱动的初始化已经完成。
以上就介绍了nginx 源码学习笔记(二十)—— event 模块一 ——初始化,包括了ioc,计数器方面的内容,希望对php教程有兴趣的朋友有所帮助。
其它类似信息

推荐信息