一 概述
本章谈谈nginx的启动过程,当你输入命令/home/nginx/sbin/nginx -c /home/nginx/conf/nginx.conf之后,nginx背后到底都做了什么事情呢?
先看看nginx启动流程如下图所示,对整个流程有个大体的映象,然后我们结合代码来细看每个过程。
二 启动流程
1 解析命令行,处理各种参数。
相关代码如下:
if (ngx_strerror_init() != NGX_OK) {
return 1;
}
// 解析启动命令的参数
if (ngx_get_options(argc, argv) != NGX_OK) {
return 1;
}
// 如果执行的是-v,则只输出版本信息,然后退出
if (ngx_show_version) {
ngx_show_version_info();
if (!ngx_test_config) {
return 0;
}
}
/* TODO */ ngx_max_sockets = -1;
ngx_time_init();
#if (NGX_PCRE)
ngx_regex_init();
#endif
ngx_pid = ngx_getpid();
//根据nginx的安装路径,或者是-p参数来初始化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()
*/
// init_cycle只是一个临时变量,主要用来存储配置文件的路径信息,启动参数,也会用到它的log成员,来临时将日志输出到屏幕
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;
}
saved_init_cycle_pool = init_cycle.pool;
if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
return 1;
}
if (ngx_process_options(&init_cycle) != NGX_OK) {
return 1;
}
if (ngx_os_init(log) != NGX_OK) {
return 1;
}
/*
* ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
*/
if (ngx_crc32_table_init() != NGX_OK) {
return 1;
}
/*
* ngx_slab_sizes_init() requires ngx_pagesize set in ngx_os_init()
*/
ngx_slab_sizes_init();
说明:
- ngx_get_options函数主要是解析启动命令,并根据参数来初始化ngx_show_version,ngx_show_help, ngx_show_configure等全局变量。例如,如果执行的是 -v,则将ngx_show_version赋值为1,表示当前的命令是检测nignx的版本;如果执行的是-t,则将ngx_show_configure赋值为1,表示当前的命令是检测nginx配置文件的正确性,后续的启动流程中会根据这些全局变量的值来决定接下来的流程。
- ngx_log_init函数用来初始化ngx_log_t类型的log对象,这里log是一个临时的变量,它指向的fd是标准输出,即此时的输出日志是显示到屏幕上。
- 接下来是创建一个临时的ngx_cycle_t类型的变量init_cycle,然后将刚才解析的到的各参数通过ngx_save_argv()等函数保存到该变量的对应成员里。
- 当nginx是平滑升级时,旧版本的nginx会通过读取环境变量getenv(NGINX_VAR)来获取相关信息,并在ngx_add_inherited_sockets函数中对旧版本的nginx服务监听的句柄做继承处理。第一次启动nignx的时候或者是执行-s reload时,NGINX_VAR环境变量都没有值,该函数直接返回NGX_OK
2 ngx_init_cycle过程
ngx_init_cycle是nginx启动过程中非常重要的函数,它会去创建当前nginx进程真正的ngx_cycle_t成员,并调用各核心模块的create_conf,解析配置文件,并调用所有模块的init_module方法等。
2.1 创建cycle并初始化部分变量
函数摘要如下:
//这里创建的cycle才是nginx进程最终的,接下来就是将老的cycle成员依次迁移到新的cycle中
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
if (cycle == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->pool = pool;
cycle->log = log;
cycle->old_cycle = old_cycle;
cycle->conf_prefix.len = old_cycle->conf_prefix.len; //配置文件nginx.conf路径
cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);
if (cycle->conf_prefix.data == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
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;
}
// nginx 需要操作的目录,第一次启动时,默认给10的大小
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 *));
// 申请创建dump文件的内存
if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t))
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NULL;
}
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;
}
// open_files表示nginx已经打开的文件
if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NULL;
}
// 初始化共享内存ngx_shm_zone_t结构体,第一次启动时分配1个
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;
}
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);
说明: 以上过程主要是创建了ngx_cycle_t结构体,并为它的conf_file,conf_param ,conf_prefix,open_files,paths,listening等成员申请内存空间,并初始化默认值。
2.2 调用核心模块的create_conf
接下来,就是要初始化cycle里最重要的成员了conf_ctx ,它保存着所有模块存储配置项的结构体的指针。
// 申请conf_ctx的内存空间
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
if (cycle->conf_ctx == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
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;
}
ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);
if (ngx_cycle_modules(cycle) != NGX_OK) {
ngx_destroy_pool(pool);
return NULL;
}
// 调用所有核心模块的create_conf方法,即所有核心模块开始构造用于存储配置项的结构体
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}0
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
其中可以看到,这里遍历了ngx_modules[]数组,并调用了所有核心模块的create_conf方法,并将返回的结构体指针保存到conf_ctx指针数组中。其中依次执行了以下核心模块:
核心模块 | 方法名 | 执行意义 |
---|---|---|
ngx_core_module | ngx_core_module_create_conf | 创建ngx_core_conf_t,并初始化相关成员变量 |
ngx_errlog_module | 没有实现create_conf | |
ngx_openssl_module | ngx_openssl_create_conf | 创建ngx_openssl_conf_t |
ngx_regex_module | ngx_regex_create_conf | 创建ngx_regex_conf_t,初始化成员变量 |
ngx_events_module | 没有实现create_conf | |
ngx_http_module | 没有实现create_conf |
从上面可以看到,虽然ngx_modules[]数组中可能包含了上百个模块,但是核心模块只有6个,而实现了create_conf方法的只有三个。
核心模块的create_conf十分简单,只是申请创建了一个用于存储该模块配置的结构体,并初始化了其中的一些成员变量,几乎没有做任何事情,通过这一阶段,cycle->conf_ctx成员变成如下形式:
其中只有0,3,4位置上有了值。由于原来的cycle->conf_ctx是四级指针void ****,因此那些还没有值的元素为void ***类型。
2.3 ngx_conf_parse解析配置文件
既然已经创建了conf_ctx结构体,那么接下来nginx开始解析nginx.conf配置文件,这是通过调用ngx_conf_parse来完成的,该函数主要会通过一个for循环,一行行的读取配置文件,每次读到一个指令,会遍历所有模块的commands数组,找到对该指令感兴趣的模块,并调用ngx_command_t中对应的handler方法来处理。
ngx_conf_parse函数原型摘要如下:
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
char *rv;
ngx_fd_t fd;
ngx_int_t rc;
ngx_buf_t buf;
ngx_conf_file_t *prev, conf_file;
enum {
parse_file = 0,
parse_block,
parse_param
} type;
#if (NGX_SUPPRESS_WARN)
fd = NGX_INVALID_FILE;
prev = NULL;
#endif
// nginx启动流程中,会在ngx_init_cycle函数中调用ngx_conf_parse来解析配置文件,此时传进来的就是filename = /home/nginx/conf/nginx.conf
if (filename) {
/* open configuration file */
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%s\" failed",
filename->data);
return NGX_CONF_ERROR;
}
prev = cf->conf_file;
cf->conf_file = &conf_file;
if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
ngx_fd_info_n " \"%s\" failed", filename->data);
}
cf->conf_file->buffer = &buf;
buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
if (buf.start == NULL) {
goto failed;
}
buf.pos = buf.start;
buf.last = buf.start;
buf.end = buf.last + NGX_CONF_BUFFER;
buf.temporary = 1;
cf->conf_file->file.fd = fd;
cf->conf_file->file.name.len = filename->len;
cf->conf_file->file.name.data = filename->data;
cf->conf_file->file.offset = 0;
cf->conf_file->file.log = cf->log;
cf->conf_file->line = 1;
type = parse_file;
if (ngx_dump_config
#if (NGX_DEBUG)
|| 1
#endif
)
{
if (ngx_conf_add_dump(cf, filename) != NGX_OK) {
goto failed;
}
} else {
cf->conf_file->dump = NULL;
}
} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
type = parse_block; //块解析,说明此时解析的是某个块,比如upstream块
} else {
type = parse_param;
}
// 循环读取配置
for ( ;; ) {
rc = ngx_conf_read_token(cf);
/*
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/
if (rc == NGX_ERROR) {
goto done;
}
if (rc == NGX_CONF_BLOCK_DONE) {
if (type != parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
goto failed;
}
goto done;
}
if (rc == NGX_CONF_FILE_DONE) {
if (type == parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting \"}\"");
goto failed;
}
goto done;
}
if (rc == NGX_CONF_BLOCK_START) {
if (type == parse_param) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"block directives are not supported "
"in -g option");
goto failed;
}
}
/* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
// 解析upstream的时候,这个handler是null;解析配置文件时,这个handler为null
if (cf->handler) {
/*
* the custom handler, i.e., that is used in the http's
* "types { ... }" directive
*/
if (rc == NGX_CONF_BLOCK_START) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\"");
goto failed;
}
rv = (*cf->handler)(cf, NULL, cf->handler_conf);
if (rv == NGX_CONF_OK) {
continue;
}
if (rv == NGX_CONF_ERROR) {
goto failed;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
goto failed;
}
// 这里会去在所有模块的commands数组里去找,多数情况下会走这里
rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) {
goto failed;
}
}
failed:
rc = NGX_ERROR;
done:
if (filename) {
if (cf->conf_file->buffer->start) {
ngx_free(cf->conf_file->buffer->start);
}
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " %s failed",
filename->data);
rc = NGX_ERROR;
}
cf->conf_file = prev;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
这里每次循环里,都会解析一个配置项,最终会调用ngx_conf_handler来遍历所有模块,找到对该配置感兴趣的模块。
ngx_conf_handler函数摘要如下:
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
char *rv;
void *conf, **confp;
ngx_uint_t i, found;
ngx_str_t *name;
ngx_command_t *cmd;
name = cf->args->elts;
found = 0;
// 查找所有模块的command数组,找到对应的指令处理函数
for (i = 0; cf->cycle->modules[i]; i++) {
cmd = cf->cycle->modules[i]->commands;
if (cmd == NULL) {
continue;
}
//查找每个模块的command数组里的每个指令name
for ( /* void */ ; cmd->name.len; cmd++) {
// 先进行指令的长度比较,这样更快
if (name->len != cmd->name.len) {
continue;
}
// 长度一样,则name才有可能相同,继续比较name
if (ngx_strcmp(name->data, cmd->name.data) != 0) {
continue;
}
// 到这里找到了能处理改配置指令的模块
found = 1;
....
/* set up the directive's configuration context */
// 这里重新初始化conf,即每解析完一个指令之后,都要创建一个行、新的conf,为了及时更新当前配置的上下文
conf = NULL;
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf); //?
if (confp) {
conf = confp[cf->cycle->modules[i]->ctx_index];
}
}
rv = cmd->set(cf, cmd, conf); // 这里就就是调用了cmd里的set函数来处理配置项
if (rv == NGX_CONF_OK) {
return NGX_OK;
}
....
}
其中cmd->set就是指ngx_command_t数组中对应该指令的处理函数。
例如,当解析遇到"http"字样时,会找到对应感兴趣的模块是ngx_http_module,该模块对应的set函数是ngx_http_block,再这个函数中,会创建存储http配置的结构体 ngx_http_conf_ctx_t,并调用所有http模块的create_main,create_srv,_create_loc等方法。
下面是解析不同的指令,nginx对应会找到的模块,以及commands数组里对该指令设置的set函数。
指令名 | 模块名 | set函数 |
---|---|---|
event | ngx_event_module | |
http | ngx_http_module | ngx_http_block |
ngx_master_process_cycle
该方法展示了master进程是如何工作的
1 载入信号量
master不需要处理网络事件,只需要通过管理worker进程来实现重启服务,平滑升级,更换日志文件,配置文件等等功能。
nginx检查以下标志位:
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); //重新读取配置文件
sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); //重新打开服务中的所有文件
sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); //告诉所有的子进程不要接收新的请求了,相当于QUIT信号,优雅的关闭
sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); //强制关闭整个服务
sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); //平滑的升级nginx,-USR2信号
2 启动woker进程和cache manage进程
调用ngx_start_worker_processes启动worker进程、调用ngx_start_cache_manager_processes启动cache manager进程
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
2.1 ngx_start_worker_processes源码如下:
static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
ngx_int_t i;
ngx_channel_t ch;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
ngx_memzero(&ch, sizeof(ngx_channel_t));
ch.command = NGX_CMD_OPEN_CHANNEL;
// n表示要生成的woker子进程个数
for (i = 0; i < n; i++) {
//ngx_spawn_process封装了fork()系统调用,并且在ngx_processes数组中选一个元素来存储该worker进程的信息,ngx_worker_process_cycle表示该woker进程需要执行的工作循环。
ngx_spawn_process(cycle, ngx_worker_process_cycle,
(void *) (intptr_t) i, "worker process", type);
ch.pid = ngx_processes[ngx_process_slot].pid;
ch.slot = ngx_process_slot;
ch.fd = ngx_processes[ngx_process_slot].channel[0];
ngx_pass_open_channel(cycle, &ch);
}
}
2.2 ngx_start_cache_manager_processes源码如下:
static void
ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)
{
ngx_uint_t i, manager, loader;
ngx_path_t **path;
ngx_channel_t ch;
manager = 0;
loader = 0;
// 检测ngx_cycle_t中关于的paths元素判断缓存模块的加载情况,如果有用到缓存功能,则会拉起cache manager进程
path = ngx_cycle->paths.elts;
for (i = 0; i < ngx_cycle->paths.nelts; i++) {
if (path[i]->manager) {
manager = 1;
}
if (path[i]->loader) {
loader = 1;
}
}
if (manager == 0) {
return;
}
// 封装了系统调用fork()函数,ngx_cache_manager_process_cycle是cache manager进程的工作循环
ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
&ngx_cache_manager_ctx, "cache manager process",
respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);
ngx_memzero(&ch, sizeof(ngx_channel_t));
ch.command = NGX_CMD_OPEN_CHANNEL;
ch.pid = ngx_processes[ngx_process_slot].pid;
ch.slot = ngx_process_slot;
ch.fd = ngx_processes[ngx_process_slot].channel[0];
ngx_pass_open_channel(cycle, &ch);
if (loader == 0) {
return;
}
ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
&ngx_cache_loader_ctx, "cache loader process",
respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);
ch.command = NGX_CMD_OPEN_CHANNEL;
ch.pid = ngx_processes[ngx_process_slot].pid;
ch.slot = ngx_process_slot;
ch.fd = ngx_processes[ngx_process_slot].channel[0];
ngx_pass_open_channel(cycle, &ch);
}
3 循环检测标识位
...
for ( ;; ) {
...
// 如果ngx_reap为1,则表示至少有某个worker进程意外退出了,此时需要监控所有的worker进程,调用ngx_reap_children来重新遍历ngx_processes数组,检查
//每个woker的状态,将非正常退出的worker重新拉起。最后返回一个标志位live,如果所有子进程都正常退出,则live=0,除此之外live=1
if (ngx_reap) {
ngx_reap = 0;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
live = ngx_reap_children(cycle);
}
// 如果live标识位为0并且含有ngx_terminate || ngx_quit为1,则调用ngx_master_process_exit退出master进程
if (!live && (ngx_terminate || ngx_quit)) {
ngx_master_process_exit(cycle);
}
// 如果ngx_terminate标识位为1,则向所有worker发送TERM信号,通知子进程强制退出,然后跳到for循环最开始,等待信号激活进程
if (ngx_terminate) {
if (delay == 0) {
delay = 50;
}
if (sigio) {
sigio--;
continue;
}
sigio = ccf->worker_processes + 2 /* cache processes */;
if (delay > 1000) {
ngx_signal_worker_processes(cycle, SIGKILL);
} else {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_TERMINATE_SIGNAL));
}
continue;
}
//如果ngx_quit为1,表示要优雅的退出服务,这时会向所有的worker发送QUIT信号,通知他们退出进程
if (ngx_quit) {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
ls = cycle->listening.elts;
for (n = 0; n < cycle->listening.nelts; n++) {
if (ngx_close_socket(ls[n].fd) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
ngx_close_socket_n " %V failed",
&ls[n].addr_text);
}
}
cycle->listening.nelts = 0;
continue;
}
//如果ngx_reconfigure为1,表示要重新读取配置文件,nginx不会让原先的worker进程重新读取配置文件,而是重新初始化ngx_cycle_t结构体,用它来读取新的配置
//文件,再拉起新的worker进程,销毁旧的worker进程
if (ngx_reconfigure) {
ngx_reconfigure = 0;
if (ngx_new_binary) {
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
ngx_noaccepting = 0;
continue;
}
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
cycle = ngx_init_cycle(cycle);
if (cycle == NULL) {
cycle = (ngx_cycle_t *) ngx_cycle;
continue;
}
// 调用ngx_start_worker_processes拉起一批新的worker进程,这批worker进程使用新的ngx_cycle_t
ngx_cycle = cycle;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_core_module);
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_JUST_RESPAWN);
ngx_start_cache_manager_processes(cycle, 1);
/* allow new processes to start */
ngx_msleep(100);
//将live标识位置为1,并向原先的旧worker进程发送QUIT信号,要求他们优雅的退出自己的进程
live = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
if (ngx_restart) {
ngx_restart = 0;
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
live = 1;
}
if (ngx_reopen) {
ngx_reopen = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
ngx_reopen_files(cycle, ccf->user);
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_REOPEN_SIGNAL));
}
// ngx_change_binary为1,表示要平滑升级Nginx,则调用ngx_exec_new_binary用新的子进程启动新版本的Nginx程序
if (ngx_change_binary) {
ngx_change_binary = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
}
if (ngx_noaccept) {
ngx_noaccept = 0;
ngx_noaccepting = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
}
}
注意:
每次一个循环结束后,master进程会被挂起,直到有新的信号来时才会被激活继续执行,这是和worker进程的不同点。