nginx源码分析之启动流程---主框架

一 概述

本章谈谈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_modulengx_core_module_create_conf创建ngx_core_conf_t,并初始化相关成员变量
ngx_errlog_module没有实现create_conf
ngx_openssl_modulengx_openssl_create_conf创建ngx_openssl_conf_t
ngx_regex_modulengx_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函数
eventngx_event_module
httpngx_http_modulengx_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进程的不同点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值