Nginx源码分析——master进程与worker进程(二)

一、说明

 

在前面一章,我们大致了解了nginx进程的启动,没有深入的去看进程到底做做了些什么事情,本章我们就看看master进程工作到底在做什么。

二、函数分析

./src/os/unix/ngx_process_cycle.c>ngx_master_process_cycle(ngx_cycle_t *)
 
ngx_new_binary = 0;
delay = 0;
sigio = 0;
live = 1;

for ( ;; ) {
    if (delay) {
        if (ngx_sigalrm) {
            sigio = 0;
            delay *= 2;
            ngx_sigalrm = 0;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "termination cycle: %M", delay);

        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) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "setitimer() failed");
        }
    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");

    sigsuspend(&set);

    ngx_time_update();

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "wake up, sigio %i", sigio);

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

    if (ngx_quit) {
        ngx_signal_worker_processes(cycle,
                                    ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        ngx_close_listening_sockets(cycle);

        continue;
    }

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

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

老规矩,还是一段一段分析吧,不过这块代码看起来还是比较好懂的。

if (delay) {
    if (ngx_sigalrm) {
        sigio = 0;
        delay *= 2;
        ngx_sigalrm = 0;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "termination cycle: %M", delay);

    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) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "setitimer() failed");
    }
}

setitimer是指经过指定时间之后,内核将发送SIGALRM信号给本进程。指定的时间就是itv。接收到指令之后,ngx_sigalrm就会变成1,当delay不为0的时候,会不断的翻倍。delay用来等待worker进程退出的时间,master进程接收到信号之后,会发信号给worker进程进行退出,worker进程退出需要时间,delay就是等待的时间。
sigsuspend(&set);
这个时候master进程会被挂起,当进程接受到新的信号之后,将会返回继续只是,同时将这个信号完成。上文提到过,处理与发起信号是通过ngx_init_signals执行ngx_signal_handler中的SIGALRM信号。ngx_signal_handler函数中就会将根据输入
设置下面的状态
Nginx源码分析——master进程与worker进程(二)_第1张图片

ngx_time_update();

master进程处理信息的时候,更新缓存时间。

    if (ngx_reap) {
       ngx_reap = 0;
       ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
       
       live = ngx_reap_children(cycle);
    }
在上面提到过,进程挂起的时候,会监听对应的信号,这里当收到SIGCHLD信号的时候,进入这个逻辑。SIGCHLD信号是在子进程终止的时候向父进程发的信号。
Nginx源码分析——master进程与worker进程(二)_第2张图片

 ngx_reap_children函数会遍历ngx_process数组,检查每一个子进程的状态,对于异常退出的进程重新拉起。只有当子进程全部正常退出的时候,live才会是0,其他的时候返回的live为1。意思就是如果是异常退出,这里会将进程重新拉起,如果是有接收到退出命令的,那么就正常退出。

if (!live && (ngx_terminate || ngx_quit)) {
    ngx_master_process_exit(cycle);
}
如果live为0就是子进程全部正常退出,并且根据输入的命令,如果是stop,就执行退出master进程的操作。
Nginx源码分析——master进程与worker进程(二)_第3张图片

Nginx源码分析——master进程与worker进程(二)_第4张图片

master退出的时候,会将pid的文件删掉,释放链接、释放cycle对象里面的内存空间、删除监听的事件等等,并且发出信号。
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;
}

进行终止的时候,之前提到先设置delay超时时间,如果没有超时就执行

ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_TERMINATE_SIGNAL));
发出TERM信号,正常退出worker进程
如果退出超时就执行,强制退出
ngx_signal_worker_processes(cycle, SIGKILL);
并且通过stop关闭的时候,就不关系是不是还有请求在处理,就直接关闭了,这样就会有quit命令。//TODO ngx_signal_worker_processes函数做了什么后面去填这个坑,至于什么时候。。随缘吧。。
if (ngx_quit) {
    ngx_signal_worker_processes(cycle,
                                ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    ngx_close_listening_sockets(cycle);
    continue;
}
这个就是quit命令,相对上面就是没有业务之后,才会去关闭socket,比较优雅。
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_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;
    ngx_signal_worker_processes(cycle,
                                ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
这个对应的就是reload命令,重新读取配置文件。
Nginx源码分析——master进程与worker进程(二)_第5张图片

ngx_new_binary这个标志位先不去看,直接看下面的。后续的动作就比较明显了,初始化ngx_cycle_t结构体,重新读取配置文件。启动新的worker进程跟Cache进程,然后等待启动完成,然后退去原来老的worker进程。
这里先不按照代码顺序讲,先说ngx_change_binary状态
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);
}
处理的信号是USR2。
Nginx源码分析——master进程与worker进程(二)_第6张图片
Nginx源码分析——master进程与worker进程(二)_第7张图片
nginx提供了平滑升级的能力。平滑启动之后,ngx_new_binary中保存的就是pid。所以上面的reload的时候ngx_new_binary判定是执行不到的。
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;
}

这个简单就是重启,没有对应的信号,就是在处理逻辑里面设置了ngx_restart就进行处理。

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));
}
就是对应的命令reopen,重新设置日志文件。
if (ngx_noaccept) {
    ngx_noaccept = 0;
    ngx_noaccepting = 1;
    ngx_signal_worker_processes(cycle,
    ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}

所有的进程不在接收请求,进行从容关闭进程。

三、总结

这块很多要去解决的问题就是进程之前信号通信的问题,之前对这块也不是特别的了解,所有很多都是一知半解,也希望后面对这块有更深的了解。

你可能感兴趣的:(nginx源码,nginx,运维,linux)