nginx启动_nginx启动流程详解

48eca50ddde08e66fa7384c96fa032c4.png

nginx的启动流程主要是从nginx.c文件的main()方法开始的,在这个过程中,nginx会完成诸如解析命令行参数、初始化模块索引、解析配置文件、初始化模块、启动master,worker和cache相关进程等操作。本文主要从main()方法开始,讲解其是如何完成整个流程的。

1. 流程讲解

nginx.cmain()是一个主流程方法,各个子流程都是被封装之后然后被该方法进行调用,从而完成初始化的。如下是main()方法的工作流程:

2353f325d7a4469686565fdfe8018ddd.png

上面的流程图中,容易忽略的一个流程点在于ngx_preinit_modules()方法,该方法中会对各个模块对象的index属性进行初始化,也即将其index设置为其在所有模块数组中的索引下标。这么做的原因之一在于,nginx在后续保存各个模块的配置对象的时候,也是将其保存于一个数组上的,而其存储的位置是与该配置对应的模块的位置是一致的。通过这种方式,各个模块就可以自定义读取各自模块的配置数据。

在整个初始化过程中,有一个ngx_init_cycle()方法,该方法是解析配置文件,并且初始化各个模块的核心流程。如下是该方法的工作流程:

69cc63099f41a2eb082276c7177f8d0b.png

从上面的流程可以看出,ngx_init_cycle()方法将配置的初始化分为了几个阶段:创建模块配置对象、解析配置文件、初始化模块配置、创建共享内存块、打开监听的端口、初始化各个模块。通过对整个流程的切分,nginx也为各个模块提供了对应的方法,用于对应的模块在相应的切入点实现各自定制化的逻辑。

2. 源码讲解

2.1 nginx.c主流程

对于nginx的启动流程,主要是通过调用额各个子流程所封装的方法来实现的,如下是主流程的源码:

int ngx_cdecl main(int argc, char *const *argv) {
  ngx_buf_t *b;
  ngx_log_t *log;
  ngx_uint_t i;
  ngx_cycle_t *cycle, init_cycle;
  ngx_conf_dump_t *cd;
  ngx_core_conf_t *ccf;

  ngx_debug_init();

  // 该方法的具体作用是初始化一个链表,用于存储系统的error日志
  if (ngx_strerror_init() != NGX_OK) {
    return 1;
  }

  // 该方法的主要作用是解析nginx运行时命令行传入的具体参数
  if (ngx_get_options(argc, argv) != NGX_OK) {
    return 1;
  }

  // 显示当前nginx的版本信息
  if (ngx_show_version) {
    ngx_show_version_info();

    // 这里指的是如果不进行配置文件的检测,则直接返回,因为进行配置文件的检测需要对配置文件进行解析,
    // 因而如果不需要进行解析则直接返回,否则在后续进行配置文件的解析
    if (!ngx_test_config) {
      return 0;
    }
  }

  /* TODO */ ngx_max_sockets = -1;
  // 对nginx缓存的时间信息进行初始化
  ngx_time_init();

#if (NGX_PCRE)
  // 初始化正则表达式解析所需要的相关函数
  ngx_regex_init();
#endif

  // 获取当前主进程的id
  ngx_pid = ngx_getpid();

  // 初始化一个error log的文件句柄
  log = ngx_log_init(ngx_prefix);
  if (log == NULL) {
    return 1;
  }

    /* STUB */
#if (NGX_OPENSSL)
    ngx_ssl_init(log);
#endif

  /*
   * init_cycle->log is required for signal handlers and
   * ngx_process_options()
   */
  // 创建一个ngx_cycle_t结构体,这是nginx运行所使用的一个核心结构体,用于在后续存储各种配置信息
  ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
  init_cycle.log = log;   // 指定所使用的日志
  ngx_cycle = &init_cycle;    // 将初始化的对象赋值给当前的全局对象

  // 初始化一块内存池
  init_cycle.pool = ngx_create_pool(1024, log);
  if (init_cycle.pool == NULL) {
    return 1;
  }

  // 将执行./nginx命令时传入的参数保存到ngx_cycle_t结构体中
  if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
    return 1;
  }

  // 这里主要是处理nginx的选项值,比如nginx根目录、error log目录和log的日志级别
  if (ngx_process_options(&init_cycle) != NGX_OK) {
    return 1;
  }

  // 这里主要是获取系统的一些参数,比如CPU数目、最大socket数目等
  if (ngx_os_init(log) != NGX_OK) {
    return 1;
  }

  /*
   * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
   */

  // 初始化ngx_crc32_table_short的值为ngx_crc32_table16所指定的值
  if (ngx_crc32_table_init() != NGX_OK) {
    return 1;
  }

  // 这个方法的主要作用是,获取NGINX环境变量的值,将其按照;或者:进行分割,分割后的每一个元素都是一个socket文件描述符的id,
  // 通过这种方式让当前的nginx继承这些文件描述符,然后会或者这些文件描述符的属性信息,并且将其设置到新创建的ngx_listening_t结构体的对应变量中
  if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
    return 1;
  }

  // 这里主要是对ngx_modules数组中的每个module的index属性进行赋值,通过这种方式指定当前module在整个模块中所在的位置。
  // 另外需要注意的是,在每个模块中都有ctx属性,其为一个多维数组,该属性保存了所有模块的配置信息,而当前模块的配置信息也正好在ctx数组的
  // 对应的位置
  if (ngx_preinit_modules() != NGX_OK) {
    return 1;
  }

  // 这是初始化nginx各个配置的核心方法
  cycle = ngx_init_cycle(&init_cycle);
  if (cycle == NULL) {
    if (ngx_test_config) {
      ngx_log_stderr(0, "configuration file %s test failed",
                     init_cycle.conf_file.data);
    }

    return 1;
  }

  // 如果是测试模式,则打印配置文件的测试结果,并且如果需要dump配置数据,则进行打印
  if (ngx_test_config) {
    if (!ngx_quiet_mode) {
      ngx_log_stderr(0, "configuration file %s test is successful",
                     cycle->conf_file.data);
    }

    if (ngx_dump_config) {
      cd = cycle->config_dump.elts;

      for (i = 0; i < cycle->config_dump.nelts; i++) {

        ngx_write_stdout("# configuration file ");
        (void) ngx_write_fd(ngx_stdout, cd[i].name.data,
                            cd[i].name.len);
        ngx_write_stdout(":" NGX_LINEFEED);

        b = cd[i].buffer;

        (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);
        ngx_write_stdout(NGX_LINEFEED);
      }
    }

    return 0;
  }

  // 这里的ngx_signal主要是记录了启动参数为-s时,其后面的参数值,比如reload、reopen等
  if (ngx_signal) {
    // 这里主要是根据-s后的参数,对当前的master进程发送相应的系统命令
    return ngx_signal_process(cycle, ngx_signal);
  }

  // 这里主要是读取了系统的一些数据,并且打印了出来,没有实际的作用
  ngx_os_status(cycle->log);

  ngx_cycle = cycle;

  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

  if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {
    ngx_process = NGX_PROCESS_MASTER;
  }

#if !(NGX_WIN32)

  // 这里主要是为各个信号设置其处理函数
  if (ngx_init_signals(cycle->log) != NGX_OK) {
    return 1;
  }

  if (!ngx_inherited && ccf->daemon) {
    // 如果是daemon模式,则新建一个子进程启动
    if (ngx_daemon(cycle->log) != NGX_OK) {
      return 1;
    }

    ngx_daemonized = 1;
  }

  if (ngx_inherited) {
    ngx_daemonized = 1;
  }

#endif

  // 重新写入pid到pid文件中,因为daemon模式会新建一个子进程
  if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
    return 1;
  }

  // 这里主要是将stderr输出指向当前cycle所设置的log文件中
  if (ngx_log_redirect_stderr(cycle) != NGX_OK) {
    return 1;
  }

  if (log->file->fd != ngx_stderr) {
    if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    ngx_close_file_n
                        " built-in log failed");
    }
  }

  ngx_use_stderr = 0;

  if (ngx_process == NGX_PROCESS_SINGLE) {
    // 这里是单进程模型,也即事件的处理、缓存的维护等等工作都交由master进程进行,主要用于调试
    ngx_single_process_cycle(cycle);

  } else {
    // 这里是master-worker进程模型,master负责处理客户端的各种指令,
    // 并且将相应的指令发送给worker进程进行处理
    ngx_master_process_cycle(cycle);
  }

  return 0;
}

关于最后的master和worker进程的启动流程以及其相互之间的交互方式,我们将在后续的文章中进行讲解。

2.2 ngx_init_cycle()分支流程

ngx_init_cycle()是nginx初始化过程中不可或缺的一部分,其主要对nginx的启动流程进行了切分,从而实现各个模块定制化配置的切入。如下是该方法的源码:

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


  log = old_cycle->log;

  // 创建一个内存池
  pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
  if (pool == NULL) {
    return NULL;
  }
  pool->log = log;

  // 创建一个ngx_cycle_t结构体对象
  cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
  if (cycle == NULL) {
    ngx_destroy_pool(pool);
    return NULL;
  }

  // 设置内存池,日志和旧的cycle对象等属性
  cycle->pool = pool;
  cycle->log = log;
  cycle->old_cycle = old_cycle;

  // 设置配置文件的信息
  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;
  }

  // 设置nginx的路径信息
  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;
  }

  // 设置配置文件的信息
  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);

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


  n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;

  // 初始化nginx的paths数组
  if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *))
      != NGX_OK) {
    ngx_destroy_pool(pool);
    return NULL;
  }

  // 初始化cycle->paths.elts数组长度
  ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *));

  // 初始化用于dump配置文件的ngx_conf_dump_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_str_rbtree_insert_value
  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;
  }

  // 初始化记录打开的文件的链表,默认长度为20
  if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))
      != NGX_OK) {
    ngx_destroy_pool(pool);
    return NULL;
  }


  // 计算共享内存的个数
  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;
  }

  // 获取监听的socket的数目
  n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;

  // 初始化用于存储监听的socket的数组
  if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t))
      != NGX_OK) {
    ngx_destroy_pool(pool);
    return NULL;
  }

  // 初始化用于存储监听的socket的数组元素
  ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t));

  // 初始化用于存储可服用的connection的队列
  ngx_queue_init(&cycle->reusable_connections_queue);

  // 初始化用于存储各个模块配置信息的对象
  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);

  // 申请用于存储hostname的内存空间
  cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);
  if (cycle->hostname.data == NULL) {
    ngx_destroy_pool(pool);
    return NULL;
  }

  // 将获取到的hostname转换为小写,然后存储到cycle->hostname.data中
  ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);

  // 将获ngx_modules数组复制到cycle->modules数组中
  if (ngx_cycle_modules(cycle) != NGX_OK) {
    ngx_destroy_pool(pool);
    return NULL;
  }


  for (i = 0; cycle->modules[i]; i++) {
    // 找到nginx的核心模块
    if (cycle->modules[i]->type != NGX_CORE_MODULE) {
      continue;
    }

    module = cycle->modules[i]->ctx;
    // 这里主要是初始化各个模块的配置对象的结构体。首先判断各个模块的create_conf()方法是否存在,如果存在,则调用该方法
    // 创建对应的conf结构体,然后将其赋值到cycle->conf_ctx的对应索引位置。
    // 需要注意的是,这里创建的配置对象结构体的属性值都是默认值,其并没有初始化
    if (module->create_conf) {
      rv = module->create_conf(cycle);
      if (rv == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
      }
      cycle->conf_ctx[cycle->modules[i]->index] = rv;
    }
  }


  senv = environ;


  // 创建一个用于存储配置信息的结构体,并且初始化各个参数信息
  ngx_memzero(&conf, sizeof(ngx_conf_t));
  /* STUB: init array ? */
  conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
  if (conf.args == NULL) {
    ngx_destroy_pool(pool);
    return NULL;
  }

  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.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

  if (ngx_conf_param(&conf) != NGX_CONF_OK) {
    environ = senv;
    ngx_destroy_cycle_pools(&conf);
    return NULL;
  }

  // 这里主要是对配置文件进行解析
  if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
    environ = senv;
    ngx_destroy_cycle_pools(&conf);
    return NULL;
  }

  // 如果当前执行的./nginx命令后是-t参数,并且当前不是静态模式,则打印配置文件的解析结果
  if (ngx_test_config && !ngx_quiet_mode) {
    ngx_log_stderr(0, "the configuration file %s syntax is ok",
                   cycle->conf_file.data);
  }

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

    module = cycle->modules[i]->ctx;
    // 这里主要是初始化核心模块的配置信息
    // 对于核心模块,目前主要有core、errlog、regex和events这四个,这里的init_conf()方法主要是对这些模块的配置对象中的数据
    // 设置默认值,因为定制化的值在前面的配置文件的解析过程中已经进行了设置
    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;
      }
    }
  }

  if (ngx_process == NGX_PROCESS_SIGNALLER) {
    return cycle;
  }

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

  // 如果是测试模式,则创建对应的pid文件
  if (ngx_test_config) {

    // 创建存储了pid的文件
    if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
      goto failed;
    }

    // 检查如果不是初始化cycle对象,则创建一个pid文件,并且删除旧的pid文件
  } 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);
    }
  }

  // 这里主要是检查lock file是否有操作的权限
  if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {
    goto failed;
  }

  // 这里主要是检查配置的各个文件路径是否存在,如果不存在就创建该文件夹,并且检查当前用户是否有相应的操作权限,
  // 如果权限不够,则提升当前用户的操作权限
  if (ngx_create_paths(cycle, ccf->user) != NGX_OK) {
    goto failed;
  }

  // 这里主要是如果没有指定error log文件地址,则打开一个默认的error日志配置文件
  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;
    }

    // 打开指定的文件,比如access log等
    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;
    }

#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
  }

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

    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;

    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判断主要是找到当前的共享内存与旧的cycle中的共享内存名称和大小等完全一致时,直接复用旧的共享内存
      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

        // 复用之后,对旧的共享内存进行重新初始化
        if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
            != NGX_OK) {
          goto failed;
        }

        goto shm_zone_found;
      }

      // 如果没找到,则释放旧的共享内存
      ngx_shm_free(&oshm_zone[n].shm);

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

    // 初始化新申请的共享内存
    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) {
    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++) {

      for (i = 0; i < old_cycle->listening.nelts; i++) {
        if (ls[i].ignore) {
          continue;
        }

        // 前面已经置为了0,因而不会走到if块中
        if (ls[i].remain) {
          continue;
        }

        if (ls[i].type != nls[n].type) {
          continue;
        }

        if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen,
                             ls[i].sockaddr, ls[i].socklen, 1)
            == NGX_OK) {
          // 这里主要是复用旧的监听的文件句柄
          nls[n].fd = ls[i].fd;
          nls[n].previous = &ls[i];
          ls[i].remain = 1;

          // 如果新的ngx_listening_s的backlog队列大小不等于旧的backlog队列大小,这将其listen字段设置为1,
          // listen字段的含义主要是表征当前句柄正在被监听
          if (ls[i].backlog != nls[n].backlog) {
            nls[n].listen = 1;
          }

// 这里主要是设置新的监听的ngx_listening_s结构体的deferred_accept和accept_filter属性,在TCP数据传输时,
// 如果设置了deferred_accept,那么在进行TCP三次握手之前,只有在真正接收到客户端数据之后,才会将客户端连接从backlog队列移
// 到accept队列中,从而交由NGINX处理,这样可以大大减少nginx的资源消耗
#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

// 设置reuseport属性值
#if (NGX_HAVE_REUSEPORT)
          if (nls[n].reuseport && !ls[i].reuseport) {
            nls[n].add_reuseport = 1;
          }
#endif

          break;
        }
      }

      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 {
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {
      ls[i].open = 1;
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
      if (ls[i].accept_filter) {
          ls[i].add_deferred = 1;
      }
#endif
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
      if (ls[i].deferred_accept) {
          ls[i].add_deferred = 1;
      }
#endif
    }
  }

  // 这里主要是打开各个监听的端口
  if (ngx_open_listening_sockets(cycle) != NGX_OK) {
    goto failed;
  }

  if (!ngx_test_config) {
    // 这里主要是为各个套接字设置TCP相关的一些属性
    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()方法对当前模块进行初始化
  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 */

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

    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
          && ngx_strncmp(oshm_zone[i].shm.name.data,
                         shm_zone[n].shm.name.data,
                         oshm_zone[i].shm.name.len)
             == 0) {
        goto live_shm_zone;
      }
    }

    // 这里主要是将旧的共享内存释放掉
    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 *));

    // 清理旧的cycle对象,清理方式主要是将其放到事件队列中定时进行清理
    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;
  }

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

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

3. 小结

本文首先对nginx的主流程和ngx_init_cycle()配置流程的工作原理进行了讲解,然后从源码层面对这两个流程进行了深入分析。

4. 广告

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

在项目开发过程中,企业会有很多的任务、需求、缺陷等需要进行管理,CORNERSTONE 提供敏捷、任务、需求、缺陷、测试管理、WIKI、共享文件和日历等功能模块,帮助企业完成团队协作和敏捷开发中的项目管理需求;更有甘特图、看板、思维导图、燃尽图等多维度视图,帮助企业全面把控项目情况。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
nginx的try_files指令用于在处理请求时进行文件查找并执行不同的操作。它的语法如下: ``` try_files file ... uri; ``` 其中,`file`是要查找的文件路径,可以是绝对路径或相对路径。`uri`是要重写的URI或转发的URI。 当接收到一个请求时,Nginx会按照try_files指令中的顺序依次查找文件。如果找到了指定的文件,Nginx会返回该文件的内容。如果找不到文件,则会将请求重写为指定的URI,并继续查找。如果最后一个URI也找不到文件,则会返回404错误页面。 举个例子,假设我们有一个目录结构如下: ``` - /var/www/html/ - index.html - images/ - logo.png ``` 我们可以使用try_files指令来配置Nginx: ``` location / { try_files $uri $uri/ /index.html; } location /images/ { try_files $uri $uri/ =404; } ``` 在上面的配置中,如果请求的URI匹配到`/images/`,Nginx会先尝试查找该URI对应的文件。如果找到了文件,比如`/images/logo.png`存在,则会返回该文件。如果找不到文件,则会继续尝试查找`/images/logo.png/`这样的目录。如果目录也不存在,则会返回404错误。 对于其他请求,比如`/about`,Nginx会先尝试查找该URI对应的文件。如果找到了文件,比如`/var/www/html/about`存在,则会返回该文件。如果找不到文件,则会继续尝试查找`/var/www/html/about/`这样的目录。如果目录也不存在,则会返回`/var/www/html/index.html`文件的内容。 总之,try_files指令提供了一种方便的方式来处理文件查找和请求重写,使得我们能够更灵活地配置Nginx的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值