文档章节

nginx事件模块实现细节

爱宝贝丶
 爱宝贝丶
发布于 01/20 09:08
字数 4910
阅读 7.8K
收藏 3

        在nginx事件模块结构体详解中,我们讲解nginx的事件模块的整体工作流程,并且着重讲解了组织事件模块的各个方法的作用,本文则主要围绕这整个流程,从源码的角度讲解nginx事件模块的实现细节。

1. ngx_events_block()----events配置块解析

        nginx在解析nginx.conf配置文件时,如果当前解析的配置项名称为events,并且是一个配置块,则会调用ngx_events_block()方法解析该配置块,如下是该方法的源码:

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;

  // 如果存储事件模块配置数据的配置项不为空,说明已经解析过配置项了,因而直接返回
  if (*(void **) conf) {
    return "is duplicate";
  }

  // 这里主要是计算event模块的个数,并且将各个event模块的相对顺序标记在了该模块的ctx_index属性中
  ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);

  // 创建一个存储配置项数组的指针
  ctx = ngx_pcalloc(cf->pool, sizeof(void *));
  if (ctx == NULL) {
    return NGX_CONF_ERROR;
  }

  // 为配置项指针申请数组内存
  *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
  if (*ctx == NULL) {
    return NGX_CONF_ERROR;
  }

  // 将数组值赋值到conf中,也即关联到核心配置对象ngx_cycle_t中
  *(void **) conf = ctx;

  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }

    m = cf->cycle->modules[i]->ctx;

    // 如果当前模块的create_conf()方法不为空,则调用该方法创建存储配置项的结构体
    if (m->create_conf) {
      (*ctx)[cf->cycle->modules[i]->ctx_index] = m->create_conf(cf->cycle);
      if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
        return NGX_CONF_ERROR;
      }
    }
  }

  // 这里将*cf结构体进行了复制,临时存储在pcf中,然后初始化当前的*cf结构体的模块相关的参数,
  // 以进行下一步的解析
  pcf = *cf;
  cf->ctx = ctx;
  cf->module_type = NGX_EVENT_MODULE;
  cf->cmd_type = NGX_EVENT_CONF;

  // 解析events{}配置块中的子配置项
  rv = ngx_conf_parse(cf, NULL);

  // 重新将pcf复制给*cf,以供后面返回使用
  *cf = pcf;

  if (rv != NGX_CONF_OK) {
    return rv;
  }

  // 到这里,说明events{}配置块的配置项都解析完成了,因而这里调用各个模块的init_conf()方法,
  // 进行配置项的初始化和合并工作
  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }

    m = cf->cycle->modules[i]->ctx;

    // 如果当前模块的init_conf()不为空,则调用其init_conf()方法初始化配置项
    if (m->init_conf) {
      rv = m->init_conf(cf->cycle, (*ctx)[cf->cycle->modules[i]->ctx_index]);
      if (rv != NGX_CONF_OK) {
        return rv;
      }
    }
  }

  return NGX_CONF_OK;
}

        ngx_events_block()方法主要完成的工作有如下几个:

  • 调用ngx_count_modules()方法对事件模块序号进行标记,需要注意的是,这里的排序是针对当前模块在所有事件类型模块中的顺序进行标记,并且将序号保存在各模块的ctx_index属性中,比如这里的事件类型核心模块ngx_event_core_modulectx_index就为0;
  • 为指针ctx申请内存空间,并且申请一个数组,将其地址赋值给ctx指针,这里的数组长度就为事件模块的数目。其实这里的数组就是用来保存每个事件模块的配置对象的,当前事件模块在所有事件模块中的相对位置就对应于该数组中的相对位置,这里的相对位置也即前一步中计算得到的ctx_index
  • 调用各个事件模块的create_conf()方法创建各自的配置结构体,并且将其保存在ctx指针指向的数组中;
  • 调用ngx_conf_parse()方法对配置文件继续解析,前面我们已经讲到,ngx_events_block()方法就是解析到events配置项的时候才调用的,因而这里的ngx_conf_parse()方法的调用就是继续解析events配置块的子配置项,而该方法调用完成则说明events配置块里的配置项都已经解析完成;
  • 调用各个模块的init_conf()方法对配置项进行初始化,简单的说,就是,由于在nginx.conf中只配置了部分配置项的值,而剩余的配置项就由init_conf()方法来设置默认值;

2. ngx_event_init_conf()----检查事件模块配置结构体是否正常创建

        在nginx解析完nginx.conf配置文件的所有配置项后(包括前一步中讲解的对events配置项的解析),其就会调用所有核心模块的init_conf()方法对核心模块的配置项进行初始化。这里的核心模块就包括ngx_events_module,该模块的init_conf()方法指向的就是这里的ngx_event_init_conf()方法,该方法本质上并没有做什么工作,只是检查了是否创建了存储事件模块配置项的结构体数组。如下是ngx_event_init_conf()方法的源码:

static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) {
  if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                  "no \"events\" section in configuration");
    return NGX_CONF_ERROR;
  }

  return NGX_CONF_OK;
}

        上面两个方法就是ngx_events_module核心模块的两个主要的配置方法,可以看到,这个核心模块的主要作用就是创建了一个数组,用于存储各个事件模块的配置结构体的。下面我们来看一下事件核心模块的主要方法。

3. ngx_event_core_create_conf()----创建事件核心模块配置结构体

        在第1点中我们讲到,解析events配置块的子配置项之前,会调用各个事件模块的create_conf()方法来创建其使用的存储配置数据的结构体,而后调用ngx_conf_parse()方法来解析子配置项,接着调用各个事件模块的init_conf()方法初始化各个模块配置数据的结构体。这里ngx_event_core_module_ctx就是一个事件类型的模块,其create_conf属性指向的就是ngx_event_core_create_conf()方法,而init_conf属性指向的就是ngx_event_core_init_conf()方法。这一节我们首先讲解ngx_event_core_create_conf()方法的实现原理:

static void *ngx_event_core_create_conf(ngx_cycle_t *cycle) {
  ngx_event_conf_t *ecf;

  ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
  if (ecf == NULL) {
    return NULL;
  }

  ecf->connections = NGX_CONF_UNSET_UINT;
  ecf->use = NGX_CONF_UNSET_UINT;
  ecf->multi_accept = NGX_CONF_UNSET;
  ecf->accept_mutex = NGX_CONF_UNSET;
  ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
  ecf->name = (void *) NGX_CONF_UNSET;

  return ecf;
}

        可以看到,这里的ngx_event_core_create_conf()方法本质上就是创建了一个ngx_event_conf_t结构体,并且将各个属性都设置为未设置状态。

4. ngx_event_core_init_conf()----初始化配置结构体

        前面我们讲到,在解析完各个子配置项之后,nginx会调用各个事件模块的init_conf()方法,这里的核心事件模块就是这个ngx_event_core_init_conf()方法,如下是该方法的源码:

static char * ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {
  ngx_event_conf_t *ecf = conf;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
  int                  fd;
#endif
  ngx_int_t i;
  ngx_module_t *module;
  ngx_event_module_t *event_module;

  module = NULL;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

  // 测试是否具有创建epoll句柄的权限
  fd = epoll_create(100);

  if (fd != -1) {
    // 关闭创建的epoll句柄,并且将module指向epoll模块
      (void) close(fd);
      module = &ngx_epoll_module;

  } else if (ngx_errno != NGX_ENOSYS) {
      module = &ngx_epoll_module;
  }
#endif

  // 这里,如果没有前面判断的模块类型,则默认使用事件模块中的第一个模块作为事件处理模型
  if (module == NULL) {
    for (i = 0; cycle->modules[i]; i++) {

      if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
        continue;
      }

      event_module = cycle->modules[i]->ctx;

      if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0) {
        continue;
      }

      module = cycle->modules[i];
      break;
    }
  }

  // 如果此时module还是为NULL,则返回异常
  if (module == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
    return NGX_CONF_ERROR;
  }

  // 下面的操作主要是判断各个属性是否为初始设置的无效值,如果是,则说明nginx.conf中没有配置
  // 关于该属性的配置项,那么这里就会为该属性设置默认值
  ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
  cycle->connection_n = ecf->connections;

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

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

  ngx_conf_init_value(ecf->multi_accept, 0);
  ngx_conf_init_value(ecf->accept_mutex, 0);
  ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

  return NGX_CONF_OK;
}

        ngx_event_core_init_conf()方法的主要做了两件事:

  • 选择当前所使用的模块,如果没指定,则默认使用第一个事件模块;
  • 初始化事件核心模块的配置结构体的各个属性值为默认值。

5. ngx_event_module_init()----核心模块的配置项初始化

        对于ngx_event_core_module模块而言,其还指定了两个方法,一个是用于初始化模块的ngx_event_module_init()方法,另一个是用于worker进程执行主循环逻辑之前进行调用的ngx_event_process_init()方法。ngx_event_module_init()方法是在master进程中调用的,其会在解析完nginx.conf文件中的所有配置项之后调用,本质上,该方法的作用就是对当前配置的核心模块(事件模块)进行初始化。如下是ngx_event_module_init()方法的源码:

/**
 * 当前方法的主要作用是申请一块用于存储统计数据的共享内存,然后设置ngx_accept_mutex_ptr、
 * ngx_connection_counter、ngx_temp_number等变量的地址,如果开启了slab stat,
 * 那么还会设置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以统计更多的数据
 */
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;

  // 获取core event module的配置结构体
  cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
  ecf = (*cf)[ngx_event_core_module.ctx_index];

  if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                  "using the \"%s\" event method", ecf->name);
  }

  // 获取core module的配置对象
  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

  ngx_timer_resolution = ccf->timer_resolution;

#if !(NGX_WIN32)
  {
    ngx_int_t limit;
    struct rlimit rlmt;

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed, ignored");

    } else {
      // 这里主要是检查当前事件模块配置的connections数目是否超过了操作系统限制的最大文件句柄数,
      // 或者超过了配置文件中指定的最大文件句柄数
      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 exceed "
                      "open file resource limit: %i",
                      ecf->connections, limit);
      }
    }
  }
#endif /* !(NGX_WIN32) */


  if (ccf->master == 0) {
    return NGX_OK;
  }

  if (ngx_accept_mutex_ptr) {
    return NGX_OK;
  }


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

  cl = 128;

  size = cl            /* ngx_accept_mutex */
         + cl          /* ngx_connection_counter */
         + cl;         /* ngx_temp_number */

#if (NGX_STAT_STUB)

  size += cl           /* ngx_stat_accepted */
         + cl          /* ngx_stat_handled */
         + cl          /* ngx_stat_requests */
         + cl          /* ngx_stat_active */
         + cl          /* ngx_stat_reading */
         + cl          /* ngx_stat_writing */
         + cl;         /* ngx_stat_waiting */

#endif

  // 设置共享内存的大小
  shm.size = size;
  ngx_str_set(&shm.name, "nginx_shared_zone");
  shm.log = cycle->log;

  // 为共享内存结构体申请内存块
  if (ngx_shm_alloc(&shm) != NGX_OK) {
    return NGX_ERROR;
  }

  // addr就是申请的共享内存块的地址
  shared = shm.addr;

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

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

  // 获取ngx_connection_counter的地址
  ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);

  // 将ngx_connection_counter的值设置为1
  (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

  ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                 "counter: %p, %uA",
                 ngx_connection_counter, *ngx_connection_counter);

  // 获取ngx_temp_number的地址
  ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);

  tp = ngx_timeofday();

  // 生成一个随机数
  ngx_random_number = (tp->msec << 16) + ngx_pid;

#if (NGX_STAT_STUB)

  ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
  ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
  ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
  ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
  ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
  ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
  ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);

#endif

  return NGX_OK;
}

        ngx_event_module_init()方法主要完成的工作有如下几个:

  • 获取配置的timer_resolution属性值,并将其赋值给ngx_timer_resolution属性,这个属性的作用主要是指定更新nginx缓存的时间的定时任务的执行时间间隔;
  • 获取nginx配置的文件描述符和当前操作系统的文件描述符的配置,对比两个值,从而更新当前进程所能开启的文件描述符的个数;
  • 声明一块共享内存,用于存储nginx进行统计用的各个属性的数据。

6. ngx_event_process_init()----初始化worker进程

        ngx_event_process_init()方法主要是在worker进程执行主循环之前进行初始化调用的,如下是该方法的源码:

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);

  // 判断当前如果满足三个条件,则标记当前为使用共享锁的方式:
  // 1. 当前为master-worker模式;
  // 2. 当前worker进程的数量大于1;
  // 3. 当前打开了使用共享锁的开关;
  if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
    ngx_use_accept_mutex = 1;
    ngx_accept_mutex_held = 0;
    ngx_accept_mutex_delay = ecf->accept_mutex_delay;

  } else {
    // 如果不满足上述条件,则指定不使用共享锁
    ngx_use_accept_mutex = 0;
  }

#if (NGX_WIN32)

  /*
   * disable accept mutex on win32 as it may cause deadlock if
   * grabbed by a process which can't accept connections
   */

  ngx_use_accept_mutex = 0;

#endif

  // 这里这两个队列的主要作用在于,每个worker进程在获取到共享锁之后,就会接收客户端accept事件,
  // 然后将其放入到ngx_posted_accept_events队列中,接着处理该队列中的事件,并且将客户端连接添加到
  // ngx_posted_events队列中,然后再释放锁,也就是说获取锁的worker进程只需要进行accept客户端连接,
  // 然后将锁的权限交给其他的进程,并且再自行处理接收到的连接的读写事件

  // 创建ngx_posted_accept_events队列,该队列用于接收客户端的连接事件
  ngx_queue_init(&ngx_posted_accept_events);
  // 创建ngx_posted_events队列,该队列用于处理客户端连接的读写事件
  ngx_queue_init(&ngx_posted_events);

  // 初始化一个用于存储事件的红黑树
  if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
    return NGX_ERROR;
  }

  for (m = 0; cycle->modules[m]; m++) {
    if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
      continue;
    }

    // ecf->use存储了所选用的事件模型的模块序号,这里是找到该模块
    if (cycle->modules[m]->ctx_index != ecf->use) {
      continue;
    }

    // module即为所选用的事件模型对应的模块
    module = cycle->modules[m]->ctx;

    // 调用指定事件模型的初始化方法
    if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
      /* fatal */
      exit(2);
    }

    break;
  }

#if !(NGX_WIN32)

  // ngx_timer_resolution表示发送更新时间事件的时间间隔
  // 这里表示如果设置了ngx_timer_resolution,并且没有设置定时事件。
  // ngx_event_flags是在事件模块的初始化中设置的,而且只有eventport和kqueue模型才会将
  // NGX_USE_TIMER_EVENT设置到ngx_event_flags中
  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
    struct sigaction sa;
    struct itimerval itv;

    ngx_memzero(&sa, sizeof(struct sigaction));
    // 这里的sa主要是添加下面的SIGALRM的信号监听事件,该信号的作用是每隔一段时间就会向当前进程发出
    // 当前进程收到信号之后就会调用下面的ngx_timer_signal_handler()方法,该方法中会将
    // ngx_event_timer_alarm设置为1,而后当前进程在进行事件循环的时候,判断如果
    // ngx_event_timer_alarm为1,则会更新当前进程所缓存的时间数据
    sa.sa_handler = ngx_timer_signal_handler;
    sigemptyset(&sa.sa_mask);

    // 添加SIGALRM监听信号
    if (sigaction(SIGALRM, &sa, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "sigaction(SIGALRM) failed");
      return NGX_ERROR;
    }

    // 设置时间间隔相关参数
    itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
    itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
    itv.it_value.tv_sec = ngx_timer_resolution / 1000;
    itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000;

    // 按照指定的时间间隔设置定时器
    if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "setitimer() failed");
    }
  }

  // NGX_USE_FD_EVENT表示event filter没有透明数据,并需要一个文件描述符表,其主要用于poll、/dev/poll
  if (ngx_event_flags & NGX_USE_FD_EVENT) {
    struct rlimit rlmt;

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed");
      return NGX_ERROR;
    }

    // 这里主要是初始化最大个数的ngx_connection_t结构体,将其保存在files数组中
    cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;

    cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n, cycle->log);
    if (cycle->files == NULL) {
      return NGX_ERROR;
    }
  }

#else

  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
      ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                    "the \"timer_resolution\" directive is not supported "
                    "with the configured event method, ignored");
      ngx_timer_resolution = 0;
  }

#endif

  // 申请指定个数的ngx_connection_t数组,这里的connection_n对应的是配置
  // 文件中的worker_connections所指定的大小
  cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
  if (cycle->connections == NULL) {
    return NGX_ERROR;
  }

  c = cycle->connections;

  // 申请指定个数的ngx_event_t数组,其长度与connections数组一致,
  // 这样便可以将connections数组与read_events数组进行对应
  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 < cycle->connection_n; i++) {
    rev[i].closed = 1;  // 初始状态默认读事件都是closed状态
    rev[i].instance = 1;  // 初始时初始化instance为1
  }

  // 申请指定个数的ngx_event_t数组,其长度与connections数组一致,
  // 这样便可以将connections数组与write_events数组进行对应
  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 < cycle->connection_n; i++) {
    wev[i].closed = 1;  // 初始时写事件默认也都是closed状态
  }

  i = cycle->connection_n;
  next = NULL;

  do {
    i--;

    // 将read_events和write_events数组的元素依次赋值到connections数组元素的read和write属性中,
    // 并且将connections数组组装成一个单链表
    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];
  } while (i);

  // 初始状态时,所有的connections都未被使用,因而需要存储在free_connections链表中
  cycle->free_connections = next;
  cycle->free_connection_n = cycle->connection_n;

  /* for each listening socket */

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

#if (NGX_HAVE_REUSEPORT)
    if (ls[i].reuseport && ls[i].worker != ngx_worker) {
      continue;
    }
#endif

    // 这里是为当前所监听的每一个端口都绑定一个ngx_connection_t结构体
    c = ngx_get_connection(ls[i].fd, cycle->log);

    if (c == NULL) {
      return NGX_ERROR;
    }

    c->type = ls[i].type;
    c->log = &ls[i].log;

    c->listening = &ls[i];
    ls[i].connection = c;

    rev = c->read;

    rev->log = c->log;
    // 标记accept为1,表示当前可以接收客户端的连接事件
    rev->accept = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT)
    rev->deferred_accept = ls[i].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;
      }
    }

#if (NGX_WIN32)

    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
        ngx_iocp_conf_t  *iocpcf;

        rev->handler = ngx_event_acceptex;

        if (ngx_use_accept_mutex) {
            continue;
        }

        if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
            return NGX_ERROR;
        }

        ls[i].log.handler = ngx_acceptex_log_error;

        iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
        if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }

    } else {
        rev->handler = ngx_event_accept;

        if (ngx_use_accept_mutex) {
            continue;
        }

        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }

#else

    // SOCK_STREAM表示TCP,一般都是TCP,也就是说在接收到客户端的accept事件之后,
    // 就会调用ngx_event_accept()方法处理该事件
    rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                            : ngx_event_recvmsg;

#if (NGX_HAVE_REUSEPORT)

    // 添加当前事件到事件监听队列中
    if (ls[i].reuseport) {
      if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
        return NGX_ERROR;
      }

      continue;
    }

#endif

    if (ngx_use_accept_mutex) {
      continue;
    }

#if (NGX_HAVE_EPOLLEXCLUSIVE)

    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
        && ccf->worker_processes > 1)
    {
        if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }

        continue;
    }

#endif

    // 添加当前事件到事件监听队列中
    if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
      return NGX_ERROR;
    }

#endif

  }

  return NGX_OK;
}

        这里ngx_event_process_init()方法主要完成了如下几个工作:

  • 根据所使用的进程模式和worker进程数量,配置是否使用共享锁的字段ngx_use_accept_mutex;
  • 初始化用于处理事件的ngx_posted_accept_events队列和ngx_posted_events队列;
  • 调用当前所使用的事件模型模块的init()方法,比如epoll模型在该init()方法中就会构建一个epoll句柄,以便后续往其中添加需要监听的事件;
  • 判断是否配置了ngx_timer_resolution属性,也即上一节中获取的更新nginx缓存时间的定时任务的执行频率字段,如果配置了,则创建一个定时任务以定时设置ngx_event_timer_alarm属性值;
  • 创建长度相同的connectionsread_eventswrite_events数组,并且将connections数组中每个ngx_connection_t结构体的read属性指向read_events数组中对应位置的读事件结构体,将write属性指向write_events数组中对应位置的写事件结构体,并且将所有的connections组织成单链表存储到ngx_cycle_t的free_connections属性中;
  • 为当前nginx监听的各个端口配置一个ngx_connection_t结构体,并且为其添加对应的事件监听器,以等待客户端连接的到来。

6. 小结

        本文主要讲解了nginx事件模块的初始化方式,以初始化过程中各个方法的调用顺序依次讲解了每个方法的实现原理,以及其在整个流程中所起到的作用。

7. 广告

       读者朋友如果觉得本文还不错,可以点击下面的广告链接,这可以为作者带来一定的收入,从而激励作者创作更好的文章,非常感谢!

在项目开发过程中,企业会有很多的任务、需求、缺陷等需要进行管理,CORNERSTONE 提供敏捷、任务、需求、缺陷、测试管理、WIKI、共享文件和日历等功能模块,帮助企业完成团队协作和敏捷开发中的项目管理需求;更有甘特图、看板、思维导图、燃尽图等多维度视图,帮助企业全面把控项目情况。

© 著作权归作者所有

爱宝贝丶

爱宝贝丶

粉丝 385
博文 153
码字总数 554971
作品 0
武汉
程序员
私信 提问
加载中

评论(0)

nginx源码分析——事件模块

事件模块概述 事件处理框架所要解决的问题是如何收集,管理,分发事件。这里所说的事件,主要以网络事件和定时器事件为主,而网络事件中又以TCP网络事件为主。由于网络事件与网卡中断处理程序...

hncscwc
2016/06/23
487
0
nginx模块描述,模块分类,处理流程

一,模块概述 nginx将各功能模块组织成一条链,当有请求到达的时候,请求依次经过这条链上的部分或者全部模块,进行处理。每个模块实现特定的功能。例如,实现对请求解压缩的模块,实现SSI的...

daydayup08
2016/06/28
111
0
浅谈Nginx服务器的内部核心架构设计!

一、前言 Nginx---Ngine X,是一款免费的、自由的、开源的、高性能HTTP服务器和反向代理服务器;也是一个IMAP、POP3、SMTP代理服务器;Nginx以其高性能、稳定性、丰富的功能、简单的配置和低...

Java干货分享
2018/10/09
0
0
深度好文:Nginx 是如何启动并处理 http 请求的?

很早之前就有看nginx的冲动,但是一直被一些事耽搁着,最近在繁忙之中,抽出点时间,看了下Nginx代码,发现整体上并不是很难看懂,而且刚好想学习nginx+lua开发。 nginx 在互联网公司使用很广...

高效运维
2018/08/13
0
0
浅谈Nginx服务器的内部核心架构设计!

一、前言 Nginx---Ngine X,是一款免费的、自由的、开源的、高性能HTTP服务器和反向代理服务器;也是一个IMAP、POP3、SMTP代理服务器;Nginx以其高性能、稳定性、丰富的功能、简单的配置和低...

Java填坑之路
2018/10/10
29
0

没有更多内容

加载失败,请刷新页面

加载更多

在RedisTemplate中使用scan代替keys指令

SCAN 简介 SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements): SCAN 命令用于迭代当前数据库中的...

xiaolyuh
17分钟前
38
0
Babel 插件手册   梦寐以求的文档

来源:https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md Babel 的使用就是使用一些预设配置。预设就是一组插件,比如env, stage-3 flow......

noo-noo
19分钟前
52
0
使用chrome 插件 沙拉查词 在英文pdf 中划词翻译

效果 下载crx插件 链接:https://pan.baidu.com/s/1pF72GZ7EMd5DmMdSCHo8IQ 提取码:tckm 官网 https://saladict.crimx.com/ github https://github.com/crimx/ext-saladict/releases 安装 ......

阿豪boy
24分钟前
43
0
Git 小课堂 004

rebase——变基,就是这个可能会把事情搞得一团糟的操作。 对于变基,我只能说,需要一个配合默契的团队,你们心灵想通,互相了解,然后你们会做出非常漂亮的事情。对于使用变基且几乎不会出...

我是任玉琢
32分钟前
36
0
如何修改MySQL列以允许NULL?

MySQL 5.0.45 修改表以允许列为空的语法是什么,或者替换为什么错误: ALTER mytable MODIFY mycolumn varchar(255) null; 我将手册解释为只运行上面的内容,它会重新创建列,这次允许为nul...

javail
32分钟前
39
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部