nginx的进程模型

nginx的进程模式


1 进程间的通信方式

    使用 UNIX域套接字 socketpair() 异步通讯机制: nginx在创建worker前将先调用 socketpair(int channel[2]) 然后将 channel[0-1]设置为异步通知方式,并注册evnet事件,父进程使用channel[0],子进程使用channel[1]实现双方的通讯.

1.1 创建子进程时用到的标示

/* NGX_PROCESS_XXX 用来标示创建子进程的属性.

    NGX_PROCESS_NORESPAWN:
        子进程退出时,父进程不会再次创建, 该标记用在创建 "cache loader process".

    NGX_PROCESS_JUST_SPAWN: 
        当 nginx -s reload 时, 如果还有未加载的 proxy_cache_path, 则需要再次创建 "cache loader process"加载,并用 NGX_PROCESS_JUST_SPAWN给这个进程做记号,防止 "master会向老的worker进程,老的cache manager进程,老的cache loader进程(如果存在)发送NGX_CMD_QUIT或SIGQUIT" 时,误以为这个进程是老的cache loader进程.

    NGX_PROCESS_RESPAWN: 
        子进程异常退出时,master会重新创建它, 如当worker或cache manager异常退出时,父进程会重新创建它.

    NGX_PROCESS_JUST_RESPAWN: 
        当 nginx -s reload 时, master会向老的worker进程,老的cache manager进程,老的cache loader进程(如果存在)发送 ngx_write_channel(NGX_CMD_QUIT)(如果失败则发送SIGQUIT信号);  该标记用来标记进程数组中哪些是新创建的子进程;其他的就是老的子进程.

    NGX_PROCESS_DETACHED: 热代码替换
 */
#define NGX_PROCESS_NORESPAWN     -1
#define NGX_PROCESS_JUST_SPAWN    -2
#define NGX_PROCESS_RESPAWN       -3
#define NGX_PROCESS_JUST_RESPAWN  -4
#define NGX_PROCESS_DETACHED      -5

2 都有哪些通信?
2.1 创建worker进程时
2.2 执行 nginx -s reload 时
2.3 执行 nginx -s stop 时
2.4 执行 nginx -s quit 时
2.5 执行 nginx -s reopen 时
2.6 某worker异常退出时

2.7 概要

                                   SIGHUP                                                NGX_CMD_QUIT|SIGQUIT
    ./nginx -s reload --------------> master (ngx_reload=1) ------------------------->  worker|cache_manager|cache_loader (ngx_quit=1, 处理完待读socket再退出)
  
                               SIGTERM|SIGINT                                                                          NGX_CMD_TERMINATE|SIGTERM
    ./nginx -s stop -----------------------> master (ngx_terminate=1,延迟退出时间) ----------------------------> worker|cache_manager|cache_loader (ngx_terminate=1,直接退出)
  
                              SIGQUIT                                            NGX_CMD_QUIT|SIGQUIT
    ./nginx -s quit --------------> master (ngx_quit=1) ------------------------> worker|cache_manager|cache_loader (ngx_quit=1, 处理完待读socket再退出)

                                    SIGUSER1                                                  NGX_CMD_REOPEN|SIGUSER1
    ./nginx -s reopen -----------------> master (ngx_reopen=1) -------------------------> worker|cache_manager|cache_loader (ngx_reopen=1)

3 通信的内容是什么?
    src/os/unix/ngx_process_cycle.h:
	#define NGX_CMD_OPEN_CHANNEL   1
	#define NGX_CMD_CLOSE_CHANNEL  2
	#define NGX_CMD_QUIT           3
	#define NGX_CMD_TERMINATE      4
	#define NGX_CMD_REOPEN         5

4 创建worker进程时 是如何通信的?
master(父进程):
    1> 在进程池 ngx_processes 中为 新worker进程 分配一个位置,如果进程池已满,也就是创建的进程数超过了最大值1024 nginx报错但不退出;
    2> 调用 socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) 创建UNIX域套接字对.并设置为异步模式且execve时关闭;
    3> 将 新worker进程 的{NGX_CMD_OPEN_CHANNEL, channel[0], 在进程池中的位置, PID} 通过 UNIX域套接字 告知已存在的子进程.
{
        ngx_start_worker_processes()
        {
            ch.command=NGX_CMD_OPEN_CHANNEL;
            for (i=0; i < n; i++) {
                //...
                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);
            }
        }
}

新worker进程:
    1> 关闭进程池中其他子进程的 channel[1];
    2> 关闭自身的 channel[0];
    3> 将自身的 channel[1] 注册event读事件: ngx_channel_handler().
{
    ngx_worker_process_cycle()
    {
        //...
        ngx_worker_process_init()
        {
            //...
            for (n=0; n < ngx_last_process; n++) {
                if (ngx_processes[n].pid == -1) {
                    continue;
                }

                if (n == ngx_process_slot) {
                    continue;
                }

                if (ngx_processes[n].channel[1] == -1) {
                    continue;
                }

                if (close(ngx_processes[n].channel[1]) == -1) {
                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                            "close() channel failed");
                }
            }

            if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
            }
            if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                        ngx_channel_handler)
                    == NGX_ERROR)
            {
                /* fatal */
                exit(2);
            }
        }
    }
}

    自身可以通过 进程池中其他子进程的 channel[0] 收发 其他子进程 的信息.
    自身可以通过 自身的 channel[1] 收发 master进程 的信息.
    
其他子进程:
    此时 其他子进程的 UNIX域套接字 收到 master进程的 {NGX_CMD_OPEN_CHANNEL, channel[0], 在进程池中的位置, PID}; 读事件处理函数 ngx_channel_handler()被调用;
    1> 获得 新子进程 在进程池中的位置;
    2> 保存 新子进程 的 channel[0];
    2> 保存 新子进程 的 PID.
{
    ngx_channel_handler()
    {
        for ( ;; ) {
            n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
            switch (ch.command) {
                case NGX_CMD_QUIT:
                    ngx_quit=1;
                    break;

                case NGX_CMD_TERMINATE:
                    ngx_terminate=1;
                    break;

                case NGX_CMD_REOPEN:
                    ngx_reopen=1;
                    break;

                case NGX_CMD_OPEN_CHANNEL:
                    ngx_processes[ch.slot].pid=ch.pid;
                    ngx_processes[ch.slot].channel[0]=ch.fd;
                    break;

                case NGX_CMD_CLOSE_CHANNEL:
                    if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                    }
                    ngx_processes[ch.slot].channel[0]=-1;
                    break;
            }
        }
    }
}

5 执行 nginx -s reload 时
执行 nginx -s reload 的进程:
    从main函数开始一直执行到 
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }
    从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reload 信号

master:
    1> 接收到 reload 信号, 信号处理函数 ngx_signal_handler 中将 ngx_reconfigure=1;
    2> 进入 ngx_reconfigure 状态; 调用 ngx_init_cycle() 初始化环境,重新加载配置文件, 重新创建 worker进程, cache管理进程, cache加载进程(热代码忽略);
    3> 使用 UNIX域套接字 通知老的worker进程, cache管理进程(cache加载进程此时可能存在,也可能已经退出,因为在ngx_cache_loader_process_handler() {exit(0)}), NGX_CMD_QUIT;
        这里不通知的进程有: 刚刚创建的worker进程, cache管理进程, cache加载进程, 正在退出中且本次发送信号为 shutdown的进程, 热代码加载中的进程.
    4> 如果使用 UNIX域套接字发送 NGX_CMD_QUIT 失败 ,则使用 kill 发送 SIGQUIT, 将这些进程的属性 exiting=1; 如果发送信号时某进程已经退出,则将其属性 exited=1;
    5> 过N久之后, 这些被发送信号的进程会退出, master进程会收到内核发送来的 SIGCHLD,将 ngx_reap=1; 并调用 ngx_process_get_status()回收退出的子进程;
    6> 回收子进程 ngx_process_get_status();
    7> reload状态,也会进入 ngx_reap 状态, 这个状态是否会重新创建 那些老的可再生的子进程 呢?
        首先, 重新创建 老的可再生的子进程的条件是: 
            (ngx_processes[i].exited && ngx_processes[i].respawn && !ngx_processes[i].exiting && !ngx_terminate && !ngx_quit)
        reload状态,可以通过观察进程属性 respawn, exiting, exited 的值来判断是否会重新创建 那些老的可再生的子进程. 除了 cache loader 进程的respawn=0, 其他进程 respawn =1. 故只有 exited=1 && exiting=0 时才会重建.
        初始值 exiting=0, exited=0;
        reload状态, 通知老的子进程退出有三种情况:
                <A> 使用 UNIX域套接字 发送 NGX_CMD_QUIT, 发送成功将 exiting=1, 此时 exited 还是 0. [不会重建]
                <B> 情况<A> 失败, 使用 kill 发送 SIGQUIT, 发送成功, 此时子进程还未退出, 将 exiting=1, 此时 exited 还是 0.  [不会重建]
                <C> 如果此时子进程已经退出, master 还未来得及回收,则将 exited=1, exiting=0, ngx_reap=1. [可能重建]
        我们来看这种 [可能重建] 的情况.
        master的for(;;)逻辑:
            for()
            {
                if (delay) {}
                sigsuspend();
                ngx_time_update();
                if (ngx_reap) {}
                if (!live && (ngx_terminate || ngx_quit)) {}
                if (ngx_terminate) {}
                if (ngx_quit) {}
                if (ngx_reconfigure) {}
                if (ngx_restart) {}
                if (ngx_reopen) {}
                if (ngx_change_binary) {}
                if (ngx_noaccept) {}
            }

        如果情况 <C> 出现, 说明子进程已经退出, master还未回收, 等本次 if (ngx_reconfigure) {} 会回到for(;;), sigsuspend();时会收到 SIGCHLD,进入 ngx_signal_handler() ngx_reap=1; 进入 ngx_reap 状态, 此时 exited=1, exiting=0, 会导致:
            (1) 这个进程会被创建
            (2) 这个老的进程占用的master的进程数组的位置不会释放. 也就是 pid 不会等于1, 而且该进程的属性始终 exited=1, exiting=0, 
            (3) 由于上面的 exited=1, exiting=0, 所以下次进入 ngx_reap状态 时,还会被创建, 下下次进入 ngx_reap状态 时还会被创建,如果不断的进入 ngx_reap 状态就会导致master的进程数组用完.
        最终的现象是: 你本来定义4个worker进程, 如果这种情况发生, 每reload一次,就会增加一个worker进程. 直到nginx 退出. 我觉得这是nginx的一个bug.
{
    /* 接受到 reload 信号: 1   "SIGHUP"           ngx_signal_handler    "reload" */
    ngx_signal_handler()
    {
        switch (ngx_process) {
            case NGX_PROCESS_MASTER:
            case NGX_PROCESS_SINGLE:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
                        ngx_reconfigure=1;
                        action=", reconfiguring";
                        break;
                    case SIGCHLD:
                        ngx_reap=1;
                        break;
                    //...
                }
                    //...
        }
    }
    ngx_master_process_cycle()
    {
        //...
        for (;;)
        {
            if (ngx_reap)
            {
                ngx_reap=0;
                ngx_reap_children(cycle);
            }
        if (ngx_reconfigure) {
            ngx_reconfigure=0;
            //...
            /* 重新初始化,加载配置 */
            cycle=ngx_init_cycle(cycle);
            ngx_cycle=cycle;
            ccf=(ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                   ngx_core_module);
            /* 重新创建新的 worker进程 */
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_JUST_RESPAWN);
            /* 重新创建新的 缓存池管理进程 和 缓存池加载进程 */
            ngx_start_cache_manager_processes(cycle, 1);
            /* 通知 */
            ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
            {
                switch (signo) {
                    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
                        ch.command=NGX_CMD_QUIT;
                        break;
                    default:
                        ch.command=0;
                }
                for (i=0; i < ngx_last_process; i++) {
                    /* 如果是 新创建的进程(NGX_PROCESS_JUST_RESPAWN), 正在退出的进程(ngx_processes[i]->exiting), 热代码加载的进程, 则不通知他们;
                       否则通过 UNIX域套接字 告知.
                     */
                    if (ch.command) {
                        if (ngx_write_channel(ngx_processes[i].channel[0],
                                    &ch, sizeof(ngx_channel_t), cycle->log)
                                == NGX_OK)
                        {
                            if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
                                ngx_processes[i].exiting=1; /* 将该进程的状态  exiting=1 */
                            }


                            continue;
                        }
                    }

                    if (kill(ngx_processes[i].pid, signo) == -1) {
                        if (err == NGX_ESRCH) { /* 该子进程进程为僵死进程 */
                            ngx_processes[i].exited=1;
                            ngx_processes[i].exiting=0;
                            ngx_reap=1;
                        }
                        continue;
                    }
                    /* 除了 reopen 信号外, 其他信号都会使子进程退出, 这里将那些子进程的状态改为正在退出的状态.*/
                    if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
                        ngx_processes[i].exiting=1;
                    }
                }
            }
        }
        }
    }
    ngx_process_get_status()
    {
        /* 循环回收 */
        for ( ;; ) {
            pid=waitpid(-1, &status, WNOHANG);

            /* 如果子进程持有 accept 锁, 则释放. */
            if (ngx_accept_mutex_ptr) {
                ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0);
            }

            /* 将该进程在master进程池中的属性 status=其退出码, exited=1,  */
            for (i=0; i < ngx_last_process; i++) {
                if (ngx_processes[i].pid == pid) {
                    ngx_processes[i].status=status;
                    ngx_processes[i].exited=1;
                    process=ngx_processes[i].name;
                    break;
                }
            }

            /* 打印日志: 如果子进程被信号中断,则打印出信号编号, 否则打印出其退出码*/
            //...
        }
    }
    ngx_reap_children()
    {
        ch.command=NGX_CMD_CLOSE_CHANNEL;
        for (i=0; i < ngx_last_process; i++) {
            if (ngx_processes[i].exited) {
                if (!ngx_processes[i].detached) {
                    /* 清理子进程的 UNIX域套接字 资源, 通过 UNIX域套接字告知其他存活子进程 NGX_CMD_CLOSE_CHANNEL */
                }
                //...
            } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
                live=1;
            }
        }

        return live;
    }


}

worker:
    1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 中将 ngx_quit=1; 或者 UNIX域套接字收到 NGX_CMD_QUIT 将 ngx_quit=1;
    2> 主逻辑进入 ngx_quit 状态, 将其修改进程标题为"worker process is shutting down"; 关闭监听socket,转入 ngx_exiting 状态
    3> ngx_exiting 状态中 调用连接池中所有连接的处理函数,并将其 close=1;
    4> 退出. 在 ngx_exiting 状态中 监听socket 已经关闭,意味着不会再有新的请求注册到 事件处理器 中, 如果 事件处理器 中没有任何事件了,此时worker进程就可以退出了.
{
    ngx_channel_handler()
    {
        for ( ;; ) {
            n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
            switch (ch.command) {


                case NGX_CMD_QUIT:
                        ngx_quit=1;
                            break;
            }
        }
    }
    ngx_signal_handler()
    {
        switch (ngx_process) {
            //...
            case NGX_PROCESS_WORKER:
            case NGX_PROCESS_HELPER:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
                        ngx_quit=1;
                        action=", shutting down";
                        break;
                        //...
                }
                //...
                break;
        }
    }
    ngx_worker_process_cycle()
    {
        if (ngx_exiting) {
            c=cycle->connections;
            for (cycle->connection) {
                if (c[i].fd != -1 && c[i].idle) {
                    c[i].close=1;
                    c[i].read->handler(c[i].read);
                }
            }
            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
            {
                ngx_worker_process_exit(cycle);
            }
        }

        if (ngx_quit) {
            ngx_quit=0;
            ngx_setproctitle("worker process is shutting down");


            if (!ngx_exiting) {
                ngx_close_listening_sockets(cycle);
                ngx_exiting=1; /* 将 ngx_exiting 置为有效. */
            }
        }
    }
}

cache_manager:
    1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;
    2> 进入 ngx_quit 状态, 进程直接 exit(0);
{
    ngx_cache_manager_process_cycle()
    {
        //...
        for (;;)
        {
            if (ngx_terminate || ngx_quit) {
                exit(0);
            }
        }
    }
}

cache_loader:
    和 cache_manager进程一样, 因为cache加载进程加载完毕后就自动退出了, 只有在加载完毕前执行 nginx -s stop 才会执行这里的逻辑.

6 执行 nginx -s stop 时
执行 nginx -s stop 的进程:
    从main函数开始一直执行到 
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }
    从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 stop 信号

master:
    1> 接收 stop (SIGINT 或 SIGTERM)信号, 信号处理函数 ngx_signal_handler 将  ngx_terminate=1;
    2> 进入 ngx_terminate 状态; 向子进程通过 UNIX域套接字 发送 NGX_CMD_TERMINATE, 如果发送失败,则通过 信号机制 发送 SIGTERM 信号.
    3> master为通过 setitimer() 每个子进程预留50毫秒(最大1000毫秒)的延迟退出时间, 如果一个子进程50毫秒未退出,则它的延迟退出时间增加一倍,直到进程的累计延迟退出时间超过1000毫秒,则向还未退出的子进程发送 SIGKILL 信号
    4> 回收退出的子进程. 收到 SIGCHLD, 将 ngx_reap=1; 进入 ngx_process_get_status(), exited=1, 此时 exiting=0;
    5> 进入 ngx_reap 状态,  所有回收的子进程都不会被重建, 因为 ngx_terminate=1; 这里将 pid=-1; 返回live=0
    6> 此时没有存活的子进程(即 ngx_reap 状态返回live=0) 或 ngx_terminate=1 或者 ngx_quit=1, 则master 进入 退出 状态 ngx_master_process_exit(); 调用所有模块的 exit_master() 钩子函数, 关闭 监听socket, 然后 exit(0);
{
    /* 接收信号 15  "SIGTERM"          ngx_signal_handler    "stop" */
    ngx_signal_handler()
    {
        switch (ngx_process) {
            case NGX_PROCESS_MASTER:
            case NGX_PROCESS_SINGLE:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_TERMINATE_SIGNAL):
                    case SIGINT:
                        ngx_terminate=1;
                        break;
                    case SIGALRM:
                        ngx_sigalrm=1;
                        break;
                    //...
                }
                //...
        }
    }
    ngx_master_process_cycle()
    {
        for (;;)
        {
            if (delay) {
                if (ngx_sigalrm) {
                    sigio=0;
                    delay *= 2;
                    ngx_sigalrm=0;
                }
                itv.it_interval.tv_sec=0;
                itv.it_interval.tv_usec=0;
                itv.it_value.tv_sec=delay / 1000;
                itv.it_value.tv_usec=(delay % 1000 ) * 1000;

                if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                }
            }

            sigsuspend(&set);

            ngx_time_update();
            if (ngx_reap) {
                ngx_reap = 0;
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
                live = ngx_reap_children(cycle);
            }

            if (!live && (ngx_terminate || ngx_quit)) {
                ngx_master_process_exit(cycle);
            }

        /* 退出机制: nginx -s stop
           master 收到 SIGTERM 或者 SIGINT 时, 进入 ngx_terminate 状态, 然后给所有子进程通过 UNIX域套接字发送 发送 NGX_CMD_QUIT, 通过kill() SIGTERM 信号 
           给每个子进程预留 50 毫秒的退出时间, 使用setitmer()延迟 n*50 毫秒, 当 n*50 还有子进程未退出, 则延迟时间增加为 n*2*50 以此类推. 如果子进程在 1000 毫秒内还没有全部退出,则对还在运行的子进程发送 SIGKILL.
           SIGKILL 是不能屏蔽也不能注册信号处理函数的信号, 动作为是 终止.
         */
            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;
            }
        }
    }
}

worker:
    1> 接收 master发送来的 SIGTERM 信号, 信号处理函数 ngx_signal_handler 中将 ngx_terminate=1; 或者 UNIX域套接字收到 NGX_CMD_TERMINATE, 将 ngx_terminate=1;
    2> 主逻辑进入 ngx_terminate 状态, 执行 ngx_worker_process_exit(), 调用 exit_process 钩子函数, 然后exit(0);
    3> 如果1000毫秒后还未退出,会收到master发来的 SIGKILL 信号,当时就退出了.
{
    ngx_channel_handler()
    {
        for ( ;; ) {
            n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
            switch (ch.command) {
                case NGX_CMD_TERMINATE:
                        ngx_terminate=1;
                            break;
            }
        }
    }
    ngx_signal_handler()
    {
        switch (ngx_process) {
            //...
            case NGX_PROCESS_WORKER:
            case NGX_PROCESS_HELPER:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_TERMINATE_SIGNAL):
                        ngx_terminate=1;
                        action=", shutting down";
                        break;
                        //...
                }
                //...
        }
    }
    ngx_worker_process_cycle()
    {
        //...
        ngx_process_events_and_timers(cycle);

        if (ngx_terminate) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

            ngx_worker_process_exit(cycle);
        }
    }
}

7 执行 nginx -s quit 时
执行 nginx -s quit 的进程:
    从main函数开始一直执行到 
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }

    从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 quit 信号


master:
    1> 接收 quit (SIGQUIT)信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;
    2> 进入 ngx_quit 状态, 向子进程通过 UNIX域套接字 发送 NGX_CMD_QUIT, 如果发送失败,则通过 信号机制 发送 SIGQUIT 信号.
    3> 关闭 监听socket.
    4> 回收退出的子进程. 收到 SIGCHLD, 将 ngx_reap=1; 进入 ngx_process_get_status(), exited=1, 此时 exiting=0;
    5> 进入 ngx_reap 状态,  所有回收的子进程都不会被重建, 因为 ngx_terminate=1; 这里将 pid=-1;
    6> 此时没有存活的子进程(即 ngx_reap 状态返回live=0) 或 ngx_terminate=1 或者 ngx_quit=1, 则master 进入退出状态 ngx_master_process_exit(); 调用所有模块的 exit_master() 钩子函数, 关闭 监听socket, 然后 exit(0);
{
    // 接收信号 3   "SIGQUIT"          ngx_signal_handler    "quit"
    ngx_signal_handler()
    {
        switch (ngx_process) {
            case NGX_PROCESS_MASTER:
            case NGX_PROCESS_SINGLE:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
                        ngx_quit=1;
                        break;
                    //...
                }
                //...
        }
    }
    ngx_master_process_cycle()
    {
        for (;;)
        {
            //...
            sigsuspend(&set);

            ngx_time_update();

            if (ngx_reap) {
                ngx_reap = 0;
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");


                live = ngx_reap_children(cycle);
            }

            if (!live && (ngx_terminate || ngx_quit)) {
                ngx_master_process_exit(cycle);
            }

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

worker:
    1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 中将 ngx_quit=1; 或者 UNIX域套接字收到 NGX_CMD_QUIT 将 ngx_quit=1;
    2> 主逻辑进入 ngx_quit 状态, 将其修改进程标题为"worker process is shutting down"; 关闭监听socket,转入 ngx_exiting 状态
    3> ngx_exiting 状态中 调用连接池中所有连接的处理函数,并将其 close=1;
    4> 退出. 在 ngx_exiting 状态中 监听socket 已经关闭,意味着不会再有新的请求注册到 事件处理器 中, 如果 事件处理器 中没有任何事件了,此时worker进程就可以退出了.
{
    ngx_channel_handler()
    {
        for ( ;; ) {
            n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
            switch (ch.command) {
                case NGX_CMD_QUIT:
                        ngx_quit=1;
                            break;
            }
        }
    }
    ngx_signal_handler()
    {
        switch (ngx_process) {
            //...
            case NGX_PROCESS_WORKER:
            case NGX_PROCESS_HELPER:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
                        ngx_quit=1;
                        action=", shutting down";
                        break;
                        //...
                }
                //...
        }
    }
    ngx_worker_process_cycle()
    {
        if (ngx_exiting) {
            c=cycle->connections;
            for (cycle->connection) {
                if (c[i].fd != -1 && c[i].idle) {
                    c[i].close=1;
                    c[i].read->handler(c[i].read);
                }
            }
            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
            {
                ngx_worker_process_exit(cycle);
            }
        }

        if (ngx_quit) {
            ngx_quit=0;
            ngx_setproctitle("worker process is shutting down");

            if (!ngx_exiting) {
                ngx_close_listening_sockets(cycle);
                ngx_exiting=1; /* 将 ngx_exiting 置为有效. */
            }
        }


    }
}
cache_manager:    1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;    2> 进入 ngx_quit 状态 ngx_worker_process_exit(), 调用 exit_process 钩子函数, 然后exit(0);
{
    ngx_cache_manager_process_cycle()
    {
        //...
        for (;;)
        {
            if (ngx_terminate || ngx_quit) {
                exit(0);
            }
        }
    }
}

cache_loader:
    和 cache_manager进程一样, 因为cache加载进程加载完毕后就自动退出了, 只有在加载完毕前执行 nginx -s stop 才会执行这里的逻辑.


8 执行 nginx -s reopen 时
执行 nginx -s reopen 的进程:
    从main函数开始一直执行到 
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }
    从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reopen 信号
master:
    1> 接收 reopen (SIGUSER1)信号, 信号处理函数 ngx_signal_handler 将 ngx_reopen=1;
    2> 进入 ngx_reopen 状态, 向子进程通过 UNIX域套接字 发送 NGX_CMD_REOPEN, 如果发送失败,则通过 信号机制 发送 SIGUSER1 信号.
    3> 进入 ngx_reopen_files() 重新打开 cycle->open_files 中的文件, 有: "logs/error.log", "logs/access.log".
{
    // 接收信号 10  "SIGUSR1"          ngx_signal_handler    "reopen"
    ngx_signal_handler()
    {
        switch (ngx_process) {
            case NGX_PROCESS_MASTER:
            case NGX_PROCESS_SINGLE:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_REOPEN_SIGNAL):
                        ngx_reopen=1;
                        break;
                    //...
                }
                //...
        }
    }
    ngx_master_process_cycle()
    {
        for (;;)
        {
            //...
            sigsuspend(&set);

            ngx_time_update();

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

worker:
    1> 接收 master 发送来的 SIGUSER1 信号, 信号处理函数 ngx_signal_handler 中将 ngx_reopen=1; 或者 UNIX域套接字收到 NGX_CMD_REOPEN 将 ngx_reopen=1;
    2> 进入 ngx_reopen 状态, 进入 ngx_reopen_files() 重新打开 cycle->open_files 中的文件, 有: "logs/error.log", "logs/access.log".
{
    ngx_channel_handler()
    {
        for ( ;; ) {
            n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
            switch (ch.command) {
                case NGX_CMD_REOPEN:
                        ngx_reopen=1;
                            break;
            }
        }
    }
    ngx_signal_handler()
    {
        switch (ngx_process) {
            //...
            case NGX_PROCESS_WORKER:
            case NGX_PROCESS_HELPER:
                switch (signo) {
                    //...
                    case ngx_signal_value(NGX_REOPEN_SIGNAL):
                        ngx_reopen=1;
                        action=", shutting down";
                        break;
                        //...
                }
                //...
        }
    }
    ngx_worker_process_cycle()
    {
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, -1);
        }
    }
}

9 某worker异常退出时
master:
    1> master收到内核发送来的 SIGCHLD 信号, 信号处理函数 ngx_signal_handler() 将 ngx_reap=1, 调用 ngx_process_get_status() 回收子进程,
    2> ngx_process_get_status() 回收子进程, 将  exited=1, 此时 exiting=0;
    3> 进入 ngx_reap 状态, ngx_reap_children(), 通知其他子进程该子进程已经退出,请关闭它的channel[0], 重新创建异常退出的worker进程. 重新创建 老的可再生的子进程的条件是: 
            (ngx_processes[i].exited && ngx_processes[i].respawn && !ngx_processes[i].exiting && !ngx_terminate && !ngx_quit)
        这里除了 cache_loader 子进程(respawn=0)外, worker, cache_manager子进程(respawn=1)异常退出时都会被创建.
{
    ngx_signal_handler()
    {
        switch (ngx_process) {
            case NGX_PROCESS_MASTER:
            case NGX_PROCESS_SINGLE:
                switch (signo) {
                    case SIGCHLD:
                        ngx_reap=1;
                        break;
                }
        }
        if (signo == SIGCHLD) {
            ngx_process_get_status();
        }
    }
    ngx_master_process_cycle()
    {
        for (;;)
        {
            if (ngx_reap)
            {
                ngx_reap=0;
                live=ngx_reap_children(cycle);
            }
        }
    }
    ngx_reap_children()
    {
        ch.command=NGX_CMD_CLOSE_CHANNEL;
        for (i=0; i < ngx_last_process; i++) {
            if (ngx_processes[i].exited) {

                if (!ngx_processes[i].detached) {

                    ngx_close_channel(ngx_processes[i].channel, cycle->log);

                    ngx_processes[i].channel[0]=-1;
                    ngx_processes[i].channel[1]=-1;

                    ch.pid=ngx_processes[i].pid;
                    ch.slot=i;

                    for (n=0; n < ngx_last_process; n++) {
                        if (ngx_processes[n].exited
                                || ngx_processes[n].pid == -1
                                || ngx_processes[n].channel[0] == -1)
                        {
                            continue;
                        }
                        ngx_write_channel(ngx_processes[n].channel[0],
                                &ch, sizeof(ngx_channel_t), cycle->log);
                    }
                }

                if (ngx_processes[i].respawn
                        && !ngx_processes[i].exiting
                        && !ngx_terminate
                        && !ngx_quit)
                {
                    if (ngx_spawn_process(cycle, ngx_processes[i].proc,
                                ngx_processes[i].data,
                                ngx_processes[i].name, i)
                            == NGX_INVALID_PID)
                    {
                        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                                "could not respawn %s",
                                ngx_processes[i].name);
                        continue;
                    }

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

                    live=1;

                    continue;
                }

                if (ngx_processes[i].pid == ngx_new_binary) {
                    ccf=(ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                            ngx_core_module);

                    if (ngx_rename_file((char *) ccf->oldpid.data,
                                (char *) ccf->pid.data)
                            != NGX_OK)
                    {
                        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                                ngx_rename_file_n " %s back to %s failed "
                                "after the new binary process \"%s\" exited",
                                ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);
                    }

                    ngx_new_binary=0;
                    if (ngx_noaccepting) {
                        ngx_restart=1;
                        ngx_noaccepting=0;
                    }
                }

                if (i == ngx_last_process - 1) {
                    ngx_last_process--;
                } else {
                    ngx_processes[i].pid=-1;
                }
            } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
                live=1;
            }
        }
        return live;
    }
}


worker:
    1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
    2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.


cache_manager:
    1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
    2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.


cache_loader:
    如果此时 cache_loader 还存在, 它也会收到如下:
    1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.

    2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.

----end-----

From: GS

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值