text: // annotated by chrono since 2016
  //
  // * ngx_init_cycle
  // * ngx_init_zone_pool
  // * ngx_shared_memory_add
  // * ngx_signal_process
  // * ngx_set_shutdown_timer
  
  /*
   * Copyright (C) Igor Sysoev
   * Copyright (C) Nginx, Inc.
   */
  
  
  #include <ngx_config.h>
  #include <ngx_core.h>
  #include <ngx_event.h>
  
  
  // 出错时销毁cycle里的内存池
  static void ngx_destroy_cycle_pools(ngx_conf_t *conf);
  
  // 初始化共享内存
  static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle,
      ngx_shm_zone_t *shm_zone);
  
  static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);
  static void ngx_clean_old_cycles(ngx_event_t *ev);
  
  // 设置了shutdown_timeout后就到时间就会执行
  // 使用独立的event对象 ngx_shutdown_event
  static void ngx_shutdown_timer_handler(ngx_event_t *ev);
  
  
  // nginx生命周期使用的超重要对象
  volatile ngx_cycle_t  *ngx_cycle;
  
  ngx_array_t            ngx_old_cycles;
  
  static ngx_pool_t     *ngx_temp_pool;
  
  static ngx_event_t     ngx_cleaner_event;
  
  // 关闭worker进程的超时时间使用
  // 独立的一个event对象
  // 在ngx_set_shutdown_timer()里
  static ngx_event_t     ngx_shutdown_event;
  
  // 用于main，检测配置文件的标识量
  ngx_uint_t             ngx_test_config;
  
  // 1.10, dump整个配置文件
  ngx_uint_t             ngx_dump_config;
  
  // 安静模式，不输出测试信息, in ngx_cycle.c
  ngx_uint_t             ngx_quiet_mode;
  
  
  /* STUB NAME */
  static ngx_connection_t  dumb;
  /* STUB */
  
  
  // 在main里调用,太长，以后可能会简化
  // 从old_cycle(init_cycle)里复制必要的信息，创建新cycle
  // 当reconfigure的时候old_cycle就是当前的cycle
  // 调用所有模块的init_module函数指针，初始化模块
  // 注意是在所有模块完成配置post configuration之后
  // 在打开日志、共享内存、端口之后才初始化模块
  // 此时还没有创建连接池（在init process里）
  ngx_cycle_t *
  ngx_init_cycle(ngx_cycle_t *old_cycle)
  {
      void                *rv;
      char               **senv;
      ngx_uint_t           i, n;
      ngx_log_t           *log;
      ngx_time_t          *tp;
      ngx_conf_t           conf;  //配置解析的环境结构体
      ngx_pool_t          *pool;
      ngx_cycle_t         *cycle, **old;
      ngx_shm_zone_t      *shm_zone, *oshm_zone;
      ngx_list_part_t     *part, *opart;
      ngx_open_file_t     *file;
      ngx_listening_t     *ls, *nls;
      ngx_core_conf_t     *ccf, *old_ccf;
      ngx_core_module_t   *module;
      char                 hostname[NGX_MAXHOSTNAMELEN];
  
      ngx_timezone_update();
  
      /* force localtime update with a new timezone */
  
      // 获取当前的时间结构体
      tp = ngx_timeofday();
      tp->sec = 0;
  
      // 获取当前时间
      ngx_time_update();
  
      // old_cycle就是main()里的init_cycle
      // 保存了pool/prefix等参数
      log = old_cycle->log;
  
      // 创建一个新内存池
      // 大小是16k，比临时的1k要大
      pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
      if (pool == NULL) {
          return NULL;
      }
      pool->log = log;
  
      // 用新内存池创建cycle
      cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
      if (cycle == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 设置cycle的三个基本成员
      // 注意cycle->pool就是自身所在的内存池
      cycle->pool = pool;
      cycle->log = log;
      cycle->old_cycle = old_cycle;
  
      // 从old_cycle拷贝conf_prefix
      cycle->conf_prefix.len = old_cycle->conf_prefix.len;
      cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);
      if (cycle->conf_prefix.data == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 从old_cycle拷贝prefix
      cycle->prefix.len = old_cycle->prefix.len;
      cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);
      if (cycle->prefix.data == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 从old_cycle拷贝conf_file
      cycle->conf_file.len = old_cycle->conf_file.len;
      cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);
      if (cycle->conf_file.data == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
      ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,
                  old_cycle->conf_file.len + 1);
  
      // 从old_cycle拷贝conf_param
      cycle->conf_param.len = old_cycle->conf_param.len;
      cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);
      if (cycle->conf_param.data == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
  
      // 从old_cycle拷贝paths路径数组
      n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;
  
      if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *))
          != NGX_OK)
      {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *));
  
  
      if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t))
          != NGX_OK)
      {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 初始化dump红黑树
      ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel,
                      ngx_str_rbtree_insert_value);
  
      // 打开的文件列表
      if (old_cycle->open_files.part.nelts) {
          n = old_cycle->open_files.part.nelts;
          for (part = old_cycle->open_files.part.next; part; part = part->next) {
              n += part->nelts;
          }
  
      } else {
          n = 20;
      }
  
      if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))
          != NGX_OK)
      {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
  
      // 共享内存
      // 如果不是reload直接启动那么就没有元素
      if (old_cycle->shared_memory.part.nelts) {
          n = old_cycle->shared_memory.part.nelts;
          for (part = old_cycle->shared_memory.part.next; part; part = part->next)
          {
              n += part->nelts;
          }
  
      } else {
          n = 1;
      }
  
      if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))
          != NGX_OK)
      {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 监听的端口列表
      // 如果不是reload直接启动那么就没有元素
      // 默认数组长度是10
      n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
  
      if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t))
          != NGX_OK)
      {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t));
  
  
      ngx_queue_init(&cycle->reusable_connections_queue);
  
  
      // 创建配置结构体数组，大小是总模块数量
      // 1.10之后ngx_max_module是模块数量的上限
      cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
      if (cycle->conf_ctx == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
  
      // 取当前主机的hostname
      if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {
          ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed");
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      /* on Linux gethostname() silently truncates name that does not fit */
  
      hostname[NGX_MAXHOSTNAMELEN - 1] = '\0';
      cycle->hostname.len = ngx_strlen(hostname);
  
      // 内存池分配
      cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);
      if (cycle->hostname.data == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 小写化后存入cycle->hostname
      // 注意这里，如果hostname有大小写不同这里就无法区分
      ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);
  
  
      // 内存池创建一个数组，可以容纳所有的模块，大小是ngx_max_module + 1
      // 拷贝脚本生成的静态模块数组到本cycle
      // 拷贝模块序号计数器到本cycle
      // 完成cycle的模块初始化
      if (ngx_cycle_modules(cycle) != NGX_OK) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
  
      // 初始化core模块
      for (i = 0; cycle->modules[i]; i++) {
          // 检查type，只处理core模块，数量很少
          if (cycle->modules[i]->type != NGX_CORE_MODULE) {
              continue;
          }
  
          //获取core模块的函数表
          module = cycle->modules[i]->ctx;
  
          // 创建core模块的配置结构体
          // 有的core模块可能没有这个函数，所以做一个空指针检查
          if (module->create_conf) {
              rv = module->create_conf(cycle);
              if (rv == NULL) {
                  ngx_destroy_pool(pool);
                  return NULL;
              }
              // 存储到cycle的配置数组里，用的是index，不是ctx_index
              cycle->conf_ctx[cycle->modules[i]->index] = rv;
          }
      }
  
  
      senv = environ;
  
  
      // 准备解析配置文件，先清零
      ngx_memzero(&conf, sizeof(ngx_conf_t));
  
      // 配置参数数组预存10个元素
      /* STUB: init array ? */
      conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
      if (conf.args == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
      // 配置解析还使用一个独立的内存池，大小是16k
      conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
      if (conf.temp_pool == NULL) {
          ngx_destroy_pool(pool);
          return NULL;
      }
  
  
      // 设置配置解析的环境，重点是conf.ctx，目前指向的是cycle->conf_ctx
      conf.ctx = cycle->conf_ctx;
      conf.cycle = cycle;
      conf.pool = pool;
      conf.log = log;
  
      //另一个重要参数，当前解析环境的模块类型
      conf.module_type = NGX_CORE_MODULE;
  
      // 当前解析环境的指令类型，解析时会进行判断
      conf.cmd_type = NGX_MAIN_CONF;
  
  #if 0
      log->log_level = NGX_LOG_DEBUG_ALL;
  #endif
  
      // 递归执行解析动作，各个模块允许的指令配置参数
      // 先解析-g传递的命令行参数
      if (ngx_conf_param(&conf) != NGX_CONF_OK) {
          environ = senv;
          ngx_destroy_cycle_pools(&conf);
          return NULL;
      }
  
      // 递归执行解析动作，各个模块允许的指令配置参数
      // 里面有http的post configration指针
      // 再解析配置文件
      if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
          environ = senv;
          ngx_destroy_cycle_pools(&conf);
          return NULL;
      }
  
      // 如果是-t检查配置，在这里就输出检查成功
      if (ngx_test_config && !ngx_quiet_mode) {
          ngx_log_stderr(0, "the configuration file %s syntax is ok",
                         cycle->conf_file.data);
      }
  
      // 其他类型的模块都已经配置好了，最后对core模块配置初始化
      // 如果有的参数没有明确配置，这里就调用init_conf设置默认值
      for (i = 0; cycle->modules[i]; i++) {
          // 检查type，只处理core模块，数量很少
          if (cycle->modules[i]->type != NGX_CORE_MODULE) {
              continue;
          }
  
          //获取core模块的函数表
          module = cycle->modules[i]->ctx;
  
          // 调用core模块的初始化函数
          // 有的core模块可能没有这个函数，所以做一个空指针检查
          if (module->init_conf) {
              if (module->init_conf(cycle,
                                    cycle->conf_ctx[cycle->modules[i]->index])
                  == NGX_CONF_ERROR)
              {
                  environ = senv;
                  ngx_destroy_cycle_pools(&conf);
                  return NULL;
              }
          }
      }
  
      // 如果是发送信号，那么不需要下面的打开文件等动作
      // ngx_process 定义在os/unix/ngx_process_cycle.c
      if (ngx_process == NGX_PROCESS_SIGNALLER) {
          return cycle;
      }
  
      // 获取core模块的配置
      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  
      if (ngx_test_config) {
  
          if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
              goto failed;
          }
  
      } else if (!ngx_is_init_cycle(old_cycle)) {
  
          /*
           * we do not create the pid file in the first ngx_init_cycle() call
           * because we need to write the demonized process pid
           */
  
          old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,
                                                     ngx_core_module);
          if (ccf->pid.len != old_ccf->pid.len
              || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)
          {
              /* new pid file name */
  
              if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
                  goto failed;
              }
  
              ngx_delete_pidfile(old_cycle);
          }
      }
  
  
      if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {
          goto failed;
      }
  
  
      // 创建必要的目录
      if (ngx_create_paths(cycle, ccf->user) != NGX_OK) {
          goto failed;
      }
  
  
      // 打开默认的日志文件
      // 初始化new_log
      if (ngx_log_open_default(cycle) != NGX_OK) {
          goto failed;
      }
  
      /* open the new files */
  
      // 遍历文件列表，日志文件
      part = &cycle->open_files.part;
      file = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              file = part->elts;
              i = 0;
          }
  
          if (file[i].name.len == 0) {
              continue;
          }
  
          // 因为使用了APPEND，所以多进程写文件是安全的
          // 没有调用ngx_conf_open_file
          // 是真正的打开文件，返回文件描述符
          file[i].fd = ngx_open_file(file[i].name.data,
                                     NGX_FILE_APPEND,
                                     NGX_FILE_CREATE_OR_OPEN,
                                     NGX_FILE_DEFAULT_ACCESS);
  
          ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,
                         "log: %p %d \"%s\"",
                         &file[i], file[i].fd, file[i].name.data);
  
          if (file[i].fd == NGX_INVALID_FILE) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                            ngx_open_file_n " \"%s\" failed",
                            file[i].name.data);
              goto failed;
          }
  
          // unix里设置close on exec，fork后自动关闭描述符
  #if !(NGX_WIN32)
          if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                            "fcntl(FD_CLOEXEC) \"%s\" failed",
                            file[i].name.data);
              goto failed;
          }
  #endif
      }
  
      // 之前解析配置文件已经设置了新的日志对象
      // 或者使用ngx_log_open_default使用默认的日志对象
      // 不再使用之前的旧log
      cycle->log = &cycle->new_log;
      pool->log = &cycle->new_log;
  
  
      /* create shared memory */
  
      // 创建配置文件里指定的共享内存
      part = &cycle->shared_memory.part;
  
      // 获取配置的共享内存数组
      shm_zone = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              shm_zone = part->elts;
              i = 0;
          }
  
          // 不允许0字节的共享内存
          if (shm_zone[i].shm.size == 0) {
              ngx_log_error(NGX_LOG_EMERG, log, 0,
                            "zero size shared memory zone \"%V\"",
                            &shm_zone[i].shm.name);
              goto failed;
          }
  
          shm_zone[i].shm.log = cycle->log;
  
          // 看是否有之前实例创建的共享内存
          opart = &old_cycle->shared_memory.part;
          oshm_zone = opart->elts;
  
          // 遍历old cycle链表
          for (n = 0; /* void */ ; n++) {
  
              if (n >= opart->nelts) {
                  if (opart->next == NULL) {
                      break;
                  }
                  opart = opart->next;
                  oshm_zone = opart->elts;
                  n = 0;
              }
  
              // 看名字的长度，不等直接跳过
              if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
                  continue;
              }
  
              // 名字字符串比较
              if (ngx_strncmp(shm_zone[i].shm.name.data,
                              oshm_zone[n].shm.name.data,
                              shm_zone[i].shm.name.len)
                  != 0)
              {
                  continue;
              }
  
              // 到这里就是同名的共享内存了
              // 看tag和容量是否相同，决定是否复用
              // 因为reload只是改配置文件，模块不变，所以tag不变
              if (shm_zone[i].tag == oshm_zone[n].tag
                  && shm_zone[i].shm.size == oshm_zone[n].shm.size
                  && !shm_zone[i].noreuse)
              {
                  // 复用则直接使用之前分配的内存
                  shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
  #if (NGX_WIN32)
                  shm_zone[i].shm.handle = oshm_zone[n].shm.handle;
  #endif
  
                  // 使用旧数据初始化，注意传入的data
                  if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
                      != NGX_OK)
                  {
                      goto failed;
                  }
  
                  // 结束本次循环,配置下一个共享内存
                  goto shm_zone_found;
              }
  
              break;
          }
  
          // 没有同名共享内存，需要新建一个
          // 创建共享内存，里面没有结构，只是内存空间
          if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
              goto failed;
          }
  
          // 初始化共享内存，增加管理结构
          if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
              goto failed;
          }
  
          // 回调模块自己的初始化函数,传入null，即新建
          if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
              goto failed;
          }
  
      shm_zone_found:
  
          // 结束本次循环,配置下一个共享内存
          continue;
      }
  
  
      /* handle the listening sockets */
  
      // 准备监听端口列表
      if (old_cycle->listening.nelts) {
          // 使用 new binary，从环境变量传递了之前打开的socketfd
          // 相关的函数是ngx_set_inherited_sockets/ngx_add_inherited_sockets
  
          ls = old_cycle->listening.elts;
          for (i = 0; i < old_cycle->listening.nelts; i++) {
              ls[i].remain = 0;
          }
  
          nls = cycle->listening.elts;
          for (n = 0; n < cycle->listening.nelts; n++) {
  
              // 找到之前已经打开监听的端口，直接填写socket描述符
              for (i = 0; i < old_cycle->listening.nelts; i++) {
                  // 如果之前获取参数出错就会是ignore
                  if (ls[i].ignore) {
                      continue;
                  }
  
                  // 循环开始前都不是remain
                  if (ls[i].remain) {
                      continue;
                  }
  
                  // 必须是相符的协议，tcp/udp
                  if (ls[i].type != nls[n].type) {
                      continue;
                  }
  
                  // 在cycle->listening里配置解析时已经有了此socket
                  if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen,
                                       ls[i].sockaddr, ls[i].socklen, 1)
                      == NGX_OK)
                  {
                      // 直接替换之前打开的socket描述符
                      nls[n].fd = ls[i].fd;
                      nls[n].previous = &ls[i];
  
                      // 置remain标记，已经找到，不需要再处理
                      ls[i].remain = 1;
  
                      // backlog不同，要求启动监听，重新listen设置
                      if (ls[i].backlog != nls[n].backlog) {
                          nls[n].listen = 1;
                      }
  
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
  
                      /*
                       * FreeBSD, except the most recent versions,
                       * could not remove accept filter
                       */
                      nls[n].deferred_accept = ls[i].deferred_accept;
  
                      if (ls[i].accept_filter && nls[n].accept_filter) {
                          if (ngx_strcmp(ls[i].accept_filter,
                                         nls[n].accept_filter)
                              != 0)
                          {
                              nls[n].delete_deferred = 1;
                              nls[n].add_deferred = 1;
                          }
  
                      } else if (ls[i].accept_filter) {
                          nls[n].delete_deferred = 1;
  
                      } else if (nls[n].accept_filter) {
                          nls[n].add_deferred = 1;
                      }
  #endif
  
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
  
                      if (ls[i].deferred_accept && !nls[n].deferred_accept) {
                          nls[n].delete_deferred = 1;
  
                      } else if (ls[i].deferred_accept != nls[n].deferred_accept)
                      {
                          nls[n].add_deferred = 1;
                      }
  #endif
  
  #if (NGX_HAVE_REUSEPORT)
                      if (nls[n].reuseport && !ls[i].reuseport) {
                          nls[n].add_reuseport = 1;
                      }
  #endif
  
                      break;
                  }
              }   // for old_cycle
  
              if (nls[n].fd == (ngx_socket_t) -1) {
                  nls[n].open = 1;
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
                  if (nls[n].accept_filter) {
                      nls[n].add_deferred = 1;
                  }
  #endif
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
                  if (nls[n].deferred_accept) {
                      nls[n].add_deferred = 1;
                  }
  #endif
              }
          }
  
      } else {
          // 没有old cycle，直接使用目前的监听端口列表
          ls = cycle->listening.elts;
          for (i = 0; i < cycle->listening.nelts; i++) {
  
              // 设置端口的状态是open
              ls[i].open = 1;
  
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
              if (ls[i].accept_filter) {
                  ls[i].add_deferred = 1;
              }
  #endif
              //支持defered特性则可以使用
  #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
              if (ls[i].deferred_accept) {
                  ls[i].add_deferred = 1;
              }
  #endif
          }
      }
  
      // ngx_connection.c
      // 开始监听端口，设置socket参数
      if (ngx_open_listening_sockets(cycle) != NGX_OK) {
          goto failed;
      }
  
      // 如果是-t则不配置监听端口参数
      if (!ngx_test_config) {
          // ngx_connection.c
          // 配置rcvbuf/sndbuf等监听端口的参数
          ngx_configure_listening_sockets(cycle);
      }
  
  
      /* commit the new cycle configuration */
  
      if (!ngx_use_stderr) {
          (void) ngx_log_redirect_stderr(cycle);
      }
  
      pool->log = cycle->log;
  
      // 调用所有模块的init_module函数指针，初始化模块
      // 注意是在所有模块完成配置post configuration之后
      // 在打开日志、共享内存、端口之后才初始化模块
      // 此时还没有创建连接池（在init process里）
      if (ngx_init_modules(cycle) != NGX_OK) {
          /* fatal */
          exit(1);
      }
  
  
      /* close and delete stuff that lefts from an old cycle */
  
      /* free the unnecessary shared memory */
  
      // 收尾工作
  
      // 看旧cycle里是否有共享内存需要释放
  
      opart = &old_cycle->shared_memory.part;
      oshm_zone = opart->elts;
  
      // 遍历旧共享内存列表
      for (i = 0; /* void */ ; i++) {
  
          if (i >= opart->nelts) {
              if (opart->next == NULL) {
                  goto old_shm_zone_done;
              }
              opart = opart->next;
              oshm_zone = opart->elts;
              i = 0;
          }
  
          // 在新cycle里找同名共享内存
          part = &cycle->shared_memory.part;
          shm_zone = part->elts;
  
          for (n = 0; /* void */ ; n++) {
  
              if (n >= part->nelts) {
                  if (part->next == NULL) {
                      break;
                  }
                  part = part->next;
                  shm_zone = part->elts;
                  n = 0;
              }
  
              // 名字长度不同
              if (oshm_zone[i].shm.name.len != shm_zone[n].shm.name.len) {
                  continue;
              }
  
              // 名字不同
              if (ngx_strncmp(oshm_zone[i].shm.name.data,
                              shm_zone[n].shm.name.data,
                              oshm_zone[i].shm.name.len)
                  != 0)
              {
                  continue;
              }
  
              // 都对，但要求复用
              if (oshm_zone[i].tag == shm_zone[n].tag
                  && oshm_zone[i].shm.size == shm_zone[n].shm.size
                  && !oshm_zone[i].noreuse)
              {
                  // 跳到continue，不会释放
                  goto live_shm_zone;
              }
  
              // 都对，不复用，结束查找
              break;
          }
  
          // 释放不使用的共享内存
          ngx_shm_free(&oshm_zone[i].shm);
  
      live_shm_zone:
  
          continue;
      } // 遍历旧共享内存列表
  
  old_shm_zone_done:
  
  
      /* close the unnecessary listening sockets */
  
      ls = old_cycle->listening.elts;
      for (i = 0; i < old_cycle->listening.nelts; i++) {
  
          if (ls[i].remain || ls[i].fd == (ngx_socket_t) -1) {
              continue;
          }
  
          if (ngx_close_socket(ls[i].fd) == -1) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                            ngx_close_socket_n " listening socket on %V failed",
                            &ls[i].addr_text);
          }
  
  #if (NGX_HAVE_UNIX_DOMAIN)
  
          if (ls[i].sockaddr->sa_family == AF_UNIX) {
              u_char  *name;
  
              name = ls[i].addr_text.data + sizeof("unix:") - 1;
  
              ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                            "deleting socket %s", name);
  
              if (ngx_delete_file(name) == NGX_FILE_ERROR) {
                  ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                                ngx_delete_file_n " %s failed", name);
              }
          }
  
  #endif
      }
  
  
      /* close the unnecessary open files */
  
      part = &old_cycle->open_files.part;
      file = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              file = part->elts;
              i = 0;
          }
  
          if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
              continue;
          }
  
          if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                            ngx_close_file_n " \"%s\" failed",
                            file[i].name.data);
          }
      }
  
      ngx_destroy_pool(conf.temp_pool);
  
      if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {
  
          ngx_destroy_pool(old_cycle->pool);
          cycle->old_cycle = NULL;
  
          return cycle;
      }
  
  
      if (ngx_temp_pool == NULL) {
          ngx_temp_pool = ngx_create_pool(128, cycle->log);
          if (ngx_temp_pool == NULL) {
              ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                            "could not create ngx_temp_pool");
              exit(1);
          }
  
          n = 10;
  
          if (ngx_array_init(&ngx_old_cycles, ngx_temp_pool, n,
                             sizeof(ngx_cycle_t *))
              != NGX_OK)
          {
              exit(1);
          }
  
          ngx_memzero(ngx_old_cycles.elts, n * sizeof(ngx_cycle_t *));
  
          ngx_cleaner_event.handler = ngx_clean_old_cycles;
          ngx_cleaner_event.log = cycle->log;
          ngx_cleaner_event.data = &dumb;
          dumb.fd = (ngx_socket_t) -1;
      }
  
      ngx_temp_pool->log = cycle->log;
  
      old = ngx_array_push(&ngx_old_cycles);
      if (old == NULL) {
          exit(1);
      }
      *old = old_cycle;
  
      if (!ngx_cleaner_event.timer_set) {
          ngx_add_timer(&ngx_cleaner_event, 30000);
          ngx_cleaner_event.timer_set = 1;
      }
  
      // 函数结束，返回cycle对象
      return cycle;
  
  
  failed:
  
      if (!ngx_is_init_cycle(old_cycle)) {
          old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,
                                                     ngx_core_module);
          if (old_ccf->environment) {
              environ = old_ccf->environment;
          }
      }
  
      /* rollback the new cycle configuration */
  
      part = &cycle->open_files.part;
      file = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              file = part->elts;
              i = 0;
          }
  
          if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
              continue;
          }
  
          if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                            ngx_close_file_n " \"%s\" failed",
                            file[i].name.data);
          }
      }
  
      /* free the newly created shared memory */
  
      part = &cycle->shared_memory.part;
      shm_zone = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              shm_zone = part->elts;
              i = 0;
          }
  
          if (shm_zone[i].shm.addr == NULL) {
              continue;
          }
  
          opart = &old_cycle->shared_memory.part;
          oshm_zone = opart->elts;
  
          for (n = 0; /* void */ ; n++) {
  
              if (n >= opart->nelts) {
                  if (opart->next == NULL) {
                      break;
                  }
                  opart = opart->next;
                  oshm_zone = opart->elts;
                  n = 0;
              }
  
              if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
                  continue;
              }
  
              if (ngx_strncmp(shm_zone[i].shm.name.data,
                              oshm_zone[n].shm.name.data,
                              shm_zone[i].shm.name.len)
                  != 0)
              {
                  continue;
              }
  
              if (shm_zone[i].tag == oshm_zone[n].tag
                  && shm_zone[i].shm.size == oshm_zone[n].shm.size
                  && !shm_zone[i].noreuse)
              {
                  goto old_shm_zone_found;
              }
  
              break;
          }
  
          ngx_shm_free(&shm_zone[i].shm);
  
      old_shm_zone_found:
  
          continue;
      }
  
      if (ngx_test_config) {
          ngx_destroy_cycle_pools(&conf);
          return NULL;
      }
  
      ls = cycle->listening.elts;
      for (i = 0; i < cycle->listening.nelts; i++) {
          if (ls[i].fd == (ngx_socket_t) -1 || !ls[i].open) {
              continue;
          }
  
          if (ngx_close_socket(ls[i].fd) == -1) {
              ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                            ngx_close_socket_n " %V failed",
                            &ls[i].addr_text);
          }
      }
  
      ngx_destroy_cycle_pools(&conf);
  
      return NULL;
  }
  
  
  // 出错时销毁cycle里的内存池
  static void
  ngx_destroy_cycle_pools(ngx_conf_t *conf)
  {
      ngx_destroy_pool(conf->temp_pool);
      ngx_destroy_pool(conf->pool);
  }
  
  
  // 初始化共享内存，增加管理结构
  // 初始化slab结构
  static ngx_int_t
  ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)
  {
      u_char           *file;
      ngx_slab_pool_t  *sp;
  
      // 取共享内存的地址
      // 转化为slab结构体
      sp = (ngx_slab_pool_t *) zn->shm.addr;
  
      // 已存在就复用
      // 依据nginx官方文档，shm.exists字段仅用于windows
      if (zn->shm.exists) {
  
          if (sp == sp->addr) {
              return NGX_OK;
          }
  
  #if (NGX_WIN32)
  
          /* remap at the required address */
  
          if (ngx_shm_remap(&zn->shm, sp->addr) != NGX_OK) {
              return NGX_ERROR;
          }
  
          sp = (ngx_slab_pool_t *) zn->shm.addr;
  
          if (sp == sp->addr) {
              return NGX_OK;
          }
  
  #endif
  
          ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                        "shared zone \"%V\" has no equal addresses: %p vs %p",
                        &zn->shm.name, sp->addr, sp);
          return NGX_ERROR;
      }
  
      // 设置slab的参数
  
      // 内存的结束地址
      sp->end = zn->shm.addr + zn->shm.size;
  
      // 最小左移，通常是3
      // 计算最小的分配内存大小，2^3=8字节
      sp->min_shift = 3;
  
      // 内存的开始地址
      // 在ngx_init_zone_pool时检测内存是否正确
      sp->addr = zn->shm.addr;
  
      // 有原子操作就不需要使用file锁
  #if (NGX_HAVE_ATOMIC_OPS)
  
      file = NULL;
  
  #else
  
      file = ngx_pnalloc(cycle->pool,
                         cycle->lock_file.len + zn->shm.name.len + 1);
      if (file == NULL) {
          return NGX_ERROR;
      }
  
      (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name);
  
  #endif
  
      // 共享内存锁,使用了信号量
      // 没有设置spin=-1,会使用信号量睡眠等待
      if (ngx_shmtx_create(&sp->mutex, &sp->lock, file) != NGX_OK) {
          return NGX_ERROR;
      }
  
      // 初始化slab结构
      // 按slot和page管理这块共享内存，best-fit
      ngx_slab_init(sp);
  
      return NGX_OK;
  }
  
  
  // 写pid到文件
  ngx_int_t
  ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
  {
      size_t      len;
      ngx_uint_t  create;
      ngx_file_t  file;
      u_char      pid[NGX_INT64_LEN + 2];
  
      if (ngx_process > NGX_PROCESS_MASTER) {
          return NGX_OK;
      }
  
      ngx_memzero(&file, sizeof(ngx_file_t));
  
      file.name = *name;
      file.log = log;
  
      create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;
  
      file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR,
                              create, NGX_FILE_DEFAULT_ACCESS);
  
      if (file.fd == NGX_INVALID_FILE) {
          ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                        ngx_open_file_n " \"%s\" failed", file.name.data);
          return NGX_ERROR;
      }
  
      if (!ngx_test_config) {
          len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid;
  
          if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) {
              return NGX_ERROR;
          }
      }
  
      if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
          ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                        ngx_close_file_n " \"%s\" failed", file.name.data);
      }
  
      return NGX_OK;
  }
  
  
  void
  ngx_delete_pidfile(ngx_cycle_t *cycle)
  {
      u_char           *name;
      ngx_core_conf_t  *ccf;
  
      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  
      name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data;
  
      if (ngx_delete_file(name) == NGX_FILE_ERROR) {
          ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                        ngx_delete_file_n " \"%s\" failed", name);
      }
  }
  
  
  // main()里调用，如果用了-s参数，那么就要发送reload/stop等信号
  ngx_int_t
  ngx_signal_process(ngx_cycle_t *cycle, char *sig)
  {
      ssize_t           n;
      ngx_pid_t         pid;
      ngx_file_t        file;
      ngx_core_conf_t  *ccf;
      u_char            buf[NGX_INT64_LEN + 2];
  
      ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
  
      // 获取core模块配置
      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  
      ngx_memzero(&file, sizeof(ngx_file_t));
  
      // 获取pid文件名
      file.name = ccf->pid;
      file.log = cycle->log;
  
      // 打开pid文件
      file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
                              NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);
  
      if (file.fd == NGX_INVALID_FILE) {
          ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
                        ngx_open_file_n " \"%s\" failed", file.name.data);
          return 1;
      }
  
      // 读取文件内容到buf
      n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);
  
      if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
          ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                        ngx_close_file_n " \"%s\" failed", file.name.data);
      }
  
      if (n == NGX_ERROR) {
          return 1;
      }
  
      while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
  
      // 转化为数字pid
      pid = ngx_atoi(buf, ++n);
  
      if (pid == (ngx_pid_t) NGX_ERROR) {
          ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
                        "invalid PID number \"%*s\" in \"%s\"",
                        n, buf, file.name.data);
          return 1;
      }
  
      // 调用os/unix/ngx_process.c里的函数
      return ngx_os_signal_process(cycle, sig, pid);
  
  }
  
  
  static ngx_int_t
  ngx_test_lockfile(u_char *file, ngx_log_t *log)
  {
  #if !(NGX_HAVE_ATOMIC_OPS)
      ngx_fd_t  fd;
  
      fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
                         NGX_FILE_DEFAULT_ACCESS);
  
      if (fd == NGX_INVALID_FILE) {
          ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                        ngx_open_file_n " \"%s\" failed", file);
          return NGX_ERROR;
      }
  
      if (ngx_close_file(fd) == NGX_FILE_ERROR) {
          ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                        ngx_close_file_n " \"%s\" failed", file);
      }
  
      if (ngx_delete_file(file) == NGX_FILE_ERROR) {
          ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                        ngx_delete_file_n " \"%s\" failed", file);
      }
  
  #endif
  
      return NGX_OK;
  }
  
  
  // 重新打开所有文件, logrotate
  // 收到sigusr1信号时被调用
  void
  ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)
  {
      ngx_fd_t          fd;
      ngx_uint_t        i;
      ngx_list_part_t  *part;
      ngx_open_file_t  *file;
  
      // 遍历文件链表
      // 里面存放的是日志文件
      part = &cycle->open_files.part;
      file = part->elts;
  
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              file = part->elts;
              i = 0;
          }
  
          if (file[i].name.len == 0) {
              continue;
          }
  
          // 先flush数据
          if (file[i].flush) {
              file[i].flush(&file[i], cycle->log);
          }
  
          // 重新打开日志文件
          // 因为使用了APPEND，所以多进程写文件是安全的
          fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND,
                             NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);
  
          ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                         "reopen file \"%s\", old:%d new:%d",
                         file[i].name.data, file[i].fd, fd);
  
          if (fd == NGX_INVALID_FILE) {
              ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                            ngx_open_file_n " \"%s\" failed", file[i].name.data);
              continue;
          }
  
  #if !(NGX_WIN32)
          if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {
              ngx_file_info_t  fi;
  
              if (ngx_file_info(file[i].name.data, &fi) == NGX_FILE_ERROR) {
                  ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                ngx_file_info_n " \"%s\" failed",
                                file[i].name.data);
  
                  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                      ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                    ngx_close_file_n " \"%s\" failed",
                                    file[i].name.data);
                  }
  
                  continue;
              }
  
              if (fi.st_uid != user) {
                  if (chown((const char *) file[i].name.data, user, -1) == -1) {
                      ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                    "chown(\"%s\", %d) failed",
                                    file[i].name.data, user);
  
                      if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                          ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                        ngx_close_file_n " \"%s\" failed",
                                        file[i].name.data);
                      }
  
                      continue;
                  }
              }
  
              if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) {
  
                  fi.st_mode |= (S_IRUSR|S_IWUSR);
  
                  if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) {
                      ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                    "chmod() \"%s\" failed", file[i].name.data);
  
                      if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                          ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                        ngx_close_file_n " \"%s\" failed",
                                        file[i].name.data);
                      }
  
                      continue;
                  }
              }
          }
  
          if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
              ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                            "fcntl(FD_CLOEXEC) \"%s\" failed",
                            file[i].name.data);
  
              if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                  ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                                ngx_close_file_n " \"%s\" failed",
                                file[i].name.data);
              }
  
              continue;
          }
  #endif
  
          // 关闭原来的文件
          if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
              ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                            ngx_close_file_n " \"%s\" failed",
                            file[i].name.data);
          }
  
          // 使用新的描述符
          file[i].fd = fd;
      }
  
      (void) ngx_log_redirect_stderr(cycle);
  }
  
  
  // 添加一个共享内存区域定义
  // 加入链表cycle->shared_memory
  // 同时也作为共享内存的查找函数
  ngx_shm_zone_t *
  ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
  {
      ngx_uint_t        i;
      ngx_shm_zone_t   *shm_zone;
      ngx_list_part_t  *part;
  
      part = &cf->cycle->shared_memory.part;
      shm_zone = part->elts;
  
      // 查找是否有重复定义
      for (i = 0; /* void */ ; i++) {
  
          if (i >= part->nelts) {
              if (part->next == NULL) {
                  break;
              }
              part = part->next;
              shm_zone = part->elts;
              i = 0;
          }
  
          // 比较名字长度
          if (name->len != shm_zone[i].shm.name.len) {
              continue;
          }
  
          // 比较字符串
          if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)
              != 0)
          {
              continue;
          }
  
          // 名字相同但tag不同，被别的模块用了，不允许
          if (tag != shm_zone[i].tag) {
              ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                              "the shared memory zone \"%V\" is "
                              "already declared for a different use",
                              &shm_zone[i].shm.name);
              return NULL;
          }
  
          // 传递0就是用来查找
          if (shm_zone[i].shm.size == 0) {
              shm_zone[i].shm.size = size;
          }
  
          // 名字相同，模块相同，但大小不同，也不允许
          if (size && size != shm_zone[i].shm.size) {
              ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                              "the size %uz of shared memory zone \"%V\" "
                              "conflicts with already declared size %uz",
                              size, &shm_zone[i].shm.name, shm_zone[i].shm.size);
              return NULL;
          }
  
          // 名字模块大小都相同，已经定义过了，返回之前的结构体
          return &shm_zone[i];
      }
  
      // 没有重复定义，是新的共享内存
      // 加入链表
      shm_zone = ngx_list_push(&cf->cycle->shared_memory);
  
      if (shm_zone == NULL) {
          return NULL;
      }
  
      // 各种参数
  
      // 注意data是null，在之后会被init调用赋值
      shm_zone->data = NULL;
  
      shm_zone->shm.log = cf->cycle->log;
  
      // 1.15.7新增此行
      shm_zone->shm.addr = NULL;
  
      // 共享内存的大小
      shm_zone->shm.size = size;
  
      // 共享内存的名字
      shm_zone->shm.name = *name;
  
      shm_zone->shm.exists = 0;
      shm_zone->init = NULL;
  
      // 这里绑定了tag，通常是模块的指针
      shm_zone->tag = tag;
  
      // 允许复用
      shm_zone->noreuse = 0;
  
      // 返回结构体，需要再填充自己的数据
      return shm_zone;
  }
  
  
  static void
  ngx_clean_old_cycles(ngx_event_t *ev)
  {
      ngx_uint_t     i, n, found, live;
      ngx_log_t     *log;
      ngx_cycle_t  **cycle;
  
      log = ngx_cycle->log;
      ngx_temp_pool->log = log;
  
      ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycles");
  
      live = 0;
  
      cycle = ngx_old_cycles.elts;
      for (i = 0; i < ngx_old_cycles.nelts; i++) {
  
          if (cycle[i] == NULL) {
              continue;
          }
  
          found = 0;
  
          for (n = 0; n < cycle[i]->connection_n; n++) {
              if (cycle[i]->connections[n].fd != (ngx_socket_t) -1) {
                  found = 1;
  
                  ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "live fd:%ui", n);
  
                  break;
              }
          }
  
          if (found) {
              live = 1;
              continue;
          }
  
          ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycle: %ui", i);
  
          ngx_destroy_pool(cycle[i]->pool);
          cycle[i] = NULL;
      }
  
      ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "old cycles status: %ui", live);
  
      if (live) {
          ngx_add_timer(ev, 30000);
  
      } else {
          ngx_destroy_pool(ngx_temp_pool);
          ngx_temp_pool = NULL;
          ngx_old_cycles.nelts = 0;
      }
  }
  
  
  // 设置关闭worker进程的超时时间
  // 使用独立的event对象 ngx_shutdown_event
  void
  ngx_set_shutdown_timer(ngx_cycle_t *cycle)
  {
      ngx_core_conf_t  *ccf;
  
      // 取核心配置，用里面的shutdown_timeout
      ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  
      // 有超时时间才加定时器，否则不会有超时关闭，而是一直等待
      if (ccf->shutdown_timeout) {
  
          // 设置定时器的调用函数
          ngx_shutdown_event.handler = ngx_shutdown_timer_handler;
  
          ngx_shutdown_event.data = cycle;
          ngx_shutdown_event.log = cycle->log;
  
          // 关键是可以取消的！！
          ngx_shutdown_event.cancelable = 1;
  
          // 加入定时器红黑树，时间到就会执行下面的ngx_shutdown_timer_handler
          ngx_add_timer(&ngx_shutdown_event, ccf->shutdown_timeout);
      }
  }
  
  
  // 设置了shutdown_timeout后就到时间就会执行
  static void
  ngx_shutdown_timer_handler(ngx_event_t *ev)
  {
      ngx_uint_t         i;
      ngx_cycle_t       *cycle;
      ngx_connection_t  *c;
  
      // cycle对象存储在ev->data里
      cycle = ev->data;
  
      // 从cycle里获取连接池，即所有正在处理的连接
      c = cycle->connections;
  
      // 逐个检查每个连接
      for (i = 0; i < cycle->connection_n; i++) {
  
          // 已经关闭，无读事件，是监听事件，则不处理
          if (c[i].fd == (ngx_socket_t) -1
              || c[i].read == NULL
              || c[i].read->accept
              || c[i].read->channel
              || c[i].read->resolver)
          {
              continue;
          }
  
          // 只处理收发数据的连接
          // 即通常意义上Nginx的连接
  
          ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
                         "*%uA shutdown timeout", c[i].number);
  
          // 设置标志位，强制标记为关闭且出错
          c[i].close = 1;
          c[i].error = 1;
  
          // 调用读事件的handler
          // handler里通常都会检查close和error标志位
          // 这样就会关闭连接
          // 在stream子系统里是在函数ngx_stream_write_filter里检查
          c[i].read->handler(c[i].read);
  
      }   //循环处理了所有活跃的连接，最后都因为close和error而关闭
  }
  