菜鸟学习Nginx之启动流程(2)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xxb249/article/details/84836850

上一篇介绍了启动流程中关于初始化ngx_cycle_t。由于ngx_cycle_t是Nginx核心结构,Nginx整个架构均是围绕它构建起来的。虽然用了一整篇文章介绍ngx_cycle_t,但是感觉还是有些内容没有介绍清楚。初始化ngx_cycle_t有一部分代码没有介绍,ngx_conf_parse,该函数解析配置文件nginx.conf函数。此函数就是单纯解析配置文件,里面代码比较枯燥乏味,理解上比较困难,所以没有深入阅读,也就在博文中没有体现出来。

今天继续介绍启动流程后半部分内容--服务进程启动。

一、master/worker模式

众所周知,Nginx是单线程服务进程,它为了充分利用CPU多核特性(提升吞吐量、高并发),它采用使用多进程方式并且为了保证高可用性,又采用了一个管理进程(master进程)和多个服务进程(worker进程)模式。

1.1、职责

  主要职责 备注
master

1、负责管理worker进程,例如:当worker进程异常退出,能够及时调度起新的worker进程

2、接收外部信号事件,例如:通过命令行发起平滑升级

外部进程只能通过信号方式与master进程通信,例如:命令行通过kill发送消息给master
worker

1、负责接收客户端请求,例如:处理http请求

2、接收master进程指定,例如:master进程发送Quit消息,通知worker进程优雅退出

3、处理部分信号

master与worker进程只能通过unix domain方式通信。

二、启动后台进程

我们一般启动程序,是通过登录终端,然后执行/usr/local/nginx/sbin/nginx。然后这样启动进程属于前端进程,也就是说如果不加特殊处理,当终端关闭时nginx进程也会退出。那么如何将前端进程变成后台守护进程(精灵进程)?在main函数中有如下代码:

    if (ngx_init_signals(cycle->log) != NGX_OK) {//初始化信号
        return 1;
    }
    /* fork出一个子进程,子进程为master 父进程(前端进程)退出*/
    if (!ngx_inherited && ccf->daemon) {
        if (ngx_daemon(cycle->log) != NGX_OK) {
            return 1;
        }
        ngx_daemonized = 1;
    }

    if (ngx_inherited) {
        ngx_daemonized = 1;
    }
/**
 * 生成守护进程
 */
ngx_int_t
ngx_daemon(ngx_log_t *log)
{
    int  fd;

    switch (fork()) {//创建一个子进程
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:
        break;

    default: //前端进程 直接退出
        exit(0);
    }
    //设置各种数据
    ngx_pid = ngx_getpid();

    if (setsid() == -1) {//很关键 设置新会话id 这样就与终端会话 脱离
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
        return NGX_ERROR;
    }

    umask(0);

    fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "open(\"/dev/null\") failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDIN_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
        return NGX_ERROR;
    }

#if 0
    if (dup2(fd, STDERR_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
        return NGX_ERROR;
    }
#endif

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

通过生成守护进程,这样就能够保证Nginx始终在后台运行提供服务。 

三、master进程

master进程,入口函数是ngx_master_process_cycle,该函数是在main函数调用,接下来重点分析一下该函数:

/**
 * master进程主循环函数
 */
void ngx_master_process_cycle(ngx_cycle_t *cycle)
{
    char *title;
    u_char *p;
    size_t size;
    ngx_int_t i;
    ngx_uint_t n, sigio;
    sigset_t set;/* 信号集 */
    struct itimerval itv;
    ngx_uint_t live;
    ngx_msec_t delay;
    ngx_listening_t *ls;
    ngx_core_conf_t *ccf;

    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));
    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
    /**
     * 设置信号屏蔽字
     * 参数1: 操作类型
     *        SIG_BLOCK    将set信号集与当前进程原有的信号屏蔽字,进行或操作
     *        SIG_UNBLOCK  解除set指定的信号
     *        SIG_SETMASK  将当前进程信号屏蔽字设置为set信号集。相当于重新赋值
     * 参数2: 
     * 参数3: 该参数是输出参数 返回当前进程设置的信号屏蔽字
     * 我的个人理解:
     *     信号的发生是百分之百的异步,而且可能并发产生多个信号。那么如果应用
     * 进程希望以阻塞方式对信号进行处理,那么就需要设置信号屏蔽字。
     *
     * sigprocmask函数适用于单线程的进程
     * pthread_sigmask函数适用于多线程的进程
     */
    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
    {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }

    sigemptyset(&set);
    //计算命令行参数,用于重新设置进程名称
    size = sizeof(master_process);

    for (i = 0; i < ngx_argc; i++)
    {
        size += ngx_strlen(ngx_argv[i]) + 1;
    }

    title = ngx_pnalloc(cycle->pool, size);
    if (title == NULL)
    {
        /* fatal */
        exit(2);
    }

    p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
    for (i = 0; i < ngx_argc; i++)
    {
        *p++ = ' ';
        p = ngx_cpystrn(p, (u_char *)ngx_argv[i], size);
    }

    ngx_setproctitle(title);//设置master进程名称

    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_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);//启动监控进程 默认不启动

    ngx_new_binary = 0;
    delay = 0;
    sigio = 0;
    live = 1; //表示是否活跃

这段代码逻辑并不是很复杂,对于信号这部分处理,我并不是很熟悉,我深入了解了一下,并将其写到注释中。如果还有不清楚的,建议看一下《Unix环境编程》。 上面代码中ngx_start_worker_processes函数用于创建worker进程,具体内容在下一面一小节中会详细介绍。接下来,master进程进入主循环(无限循环),代码如下:

    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才返回,执行后续代码
         */
        sigsuspend(&set);

        ngx_time_update();

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

        if (ngx_reap)
        {//当子进程异常退出时,会接收到SIGCHLD信号,因此会在调用起来一个子进程
            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)
        {//接收到TERM信号 理应立即关闭 但是Nginx采用延迟关闭方式
            if (delay == 0)
            {
                delay = 50; //50ms
            }

            if (sigio)
            {
                sigio--;
                continue;
            }

            sigio = ccf->worker_processes + 2 /* cache processes */;

            if (delay > 1000)
            {//如果延迟大于1000ms 则暴力关闭进程
                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));

            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;
        }
        /**
         * 当master进程接收到HUP信号,用于重新加载配置。例如:配置文件变化,需要
         * 更新配置
         */
        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));
        }
        /**
         * 表示重启worker进程,进入此分支的前提是master进程接收到SIGCHLD信号,即
         * worker进程异常退出
         */
        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;
        }
        /**
         * 当master进程接收到USR1信号,表明需要重新打开日志文件
         */
        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));
        }
        /**
         * 当master进程接收到USR2信号,表明进行平滑升级
         */
        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);
        }
        /**
         * master进程收到WINCH信号(通过kill发送)后,会通过channel发送QUIT消息
         * 给worker进程,当worker进程接收到QUIT消息就会优雅退出
         */
        if (ngx_noaccept)
        {
            ngx_noaccept = 0;
            ngx_noaccepting = 1;
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }

master进程处理逻辑并不复杂,毕竟master进程是比较清闲的。master进程主要处理各种信号事件,master也没有事件驱动(epoll),并且worker进程也不会主动发消息给master。 

四、worker进程

接下来看一下,worker进程。worker进程处理逻辑相比master进程就复杂了,但是这里并不想介绍特别深入,只做到点睛之笔就行。因为后面还会专题进行详细介绍。

/**
 * 创建worker进程
 * @param cycle  核心结构体 
 * @param n      worker进程数量
 * @param type   创建worker进程方式
 */
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; //发送第一个消息

    for (i = 0; i < n; i++)
    {
        //启动多个worker进程 回调函数为ngx_worker_process_cycle
        ngx_spawn_process(cycle, ngx_worker_process_cycle,
                          (void *)(intptr_t)i, "worker process", type);

        ch.pid = ngx_processes[ngx_process_slot].pid; /* 子进程id */
        ch.slot = ngx_process_slot; /* 子进程在ngx_processes数组中索引 */
        ch.fd = ngx_processes[ngx_process_slot].channel[0]; /* 父进程socketpair fd */

        ngx_pass_open_channel(cycle, &ch);//通过unix domain发送第一个消息给worker进程
    }
}

 代码比较简单,在看ngx_spawn_process函数之前,先来看一下流程图,如下:

通过流程图可知,该函数处理逻辑比较简单,下面是具体内容: 

/**
 * 生成子进程
 * @param cycle 核心结构体
 * @param proc  子进程进程函数
 * @param data  子进程进程函数,入参
 * @param name  子进程进程名称
 * @param respawn 生产子进程方式
 */
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)
{
    u_long     on;
    ngx_pid_t  pid; /* 子进程id */
    ngx_int_t  s; /* 子进程在数组ngx_processes中索引 */

    /* 确定子进程在数组ngx_processes中索引 */     
    if (respawn >= 0) {
        s = respawn;
    } else {
        for (s = 0; s < ngx_last_process; s++) {
            if (ngx_processes[s].pid == -1) {
                break;
            }
        }

        if (s == NGX_MAX_PROCESSES) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "no more than %d processes can be spawned",
                          NGX_MAX_PROCESSES);
            return NGX_INVALID_PID;
        }
    }


    if (respawn != NGX_PROCESS_DETACHED) {

        /* Solaris 9 still has no AF_LOCAL */
        /* 创建socketpair 并且设置socket 选项 */
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "socketpair() failed while spawning \"%s\"", name);
            return NGX_INVALID_PID;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                       "channel %d:%d",
                       ngx_processes[s].channel[0],
                       ngx_processes[s].channel[1]);

        if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          ngx_nonblocking_n " failed while spawning \"%s\"",
                          name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }

        if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          ngx_nonblocking_n " failed while spawning \"%s\"",
                          name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }

        on = 1;
        if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "ioctl(FIOASYNC) failed while spawning \"%s\"", name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }

        if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "fcntl(F_SETOWN) failed while spawning \"%s\"", name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }
        /* FD_CLOEXEC标志 表示当执行exec家族函数时自动关闭当前文件句柄 */
        if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
                           name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }

        if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
                           name);
            ngx_close_channel(ngx_processes[s].channel, cycle->log);
            return NGX_INVALID_PID;
        }

        ngx_channel = ngx_processes[s].channel[1];

    } else {
        ngx_processes[s].channel[0] = -1;
        ngx_processes[s].channel[1] = -1;
    }

    ngx_process_slot = s;


    pid = fork(); //创建子进程

    switch (pid) {

    case -1:
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "fork() failed while spawning \"%s\"", name);
        ngx_close_channel(ngx_processes[s].channel, cycle->log);
        return NGX_INVALID_PID;

    case 0:
        ngx_pid = ngx_getpid();//子进程 fork返回值0
        proc(cycle, data); //子进程一直循环,不会结束。当结束时子进程也就是exit
        break;

    default://父进程 fork返回值为非0 是子进程 进程id
        break;
    }
    /**
     * 以下代码是父进程执行 子进程永远不会执行到这里. 因此子进程再退出时直接
     * 调用exit 没有机会执行下列代码
     */
    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
    /* 设置子进程信息 */
    ngx_processes[s].pid = pid;
    ngx_processes[s].exited = 0;

    if (respawn >= 0) {
        return pid;
    }

    ngx_processes[s].proc = proc;
    ngx_processes[s].data = data;
    ngx_processes[s].name = name;
    ngx_processes[s].exiting = 0;

    switch (respawn) {

    case NGX_PROCESS_NORESPAWN:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_JUST_SPAWN:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 1;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_JUST_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_spawn = 1;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_DETACHED:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 1;
        break;
    }

    if (s == ngx_last_process) {
        ngx_last_process++;
    }

    return pid;
}

四、信号

信号注册是在main函数中调用,具体注册内容比较简单,如下:

/**
 * 信号注册
 */
ngx_int_t
ngx_init_signals(ngx_log_t *log)
{
    ngx_signal_t      *sig;
    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = sig->handler;/* 信号处理函数 */
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {
#if (NGX_VALGRIND)
            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                          "sigaction(%s) failed, ignored", sig->signame);
#else
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                          "sigaction(%s) failed", sig->signame);
            return NGX_ERROR;
#endif
        }
    }

    return NGX_OK;
}

当有信号事件发生,进程会发生中断,然后调用信号处理函数,如下为信号中断处理函数:

static void
ngx_signal_handler(int signo)
{
    char            *action;
    ngx_int_t        ignore;
    ngx_err_t        err;
    ngx_signal_t    *sig;

    ignore = 0;

    err = ngx_errno;

    for (sig = signals; sig->signo != 0; sig++) {
        if (sig->signo == signo) {
            break;
        }
    }

    ngx_time_sigsafe_update();

    action = "";

    switch (ngx_process) {
    /* master进程处理信号 */
    case NGX_PROCESS_MASTER:
    case NGX_PROCESS_SINGLE:
        switch (signo) {

        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            break;

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
            if (ngx_daemonized) {
                ngx_noaccept = 1;
                action = ", stop accepting connections";
            }
            break;

        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
            ngx_reconfigure = 1;
            action = ", reconfiguring";
            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):
            ngx_reopen = 1;
            action = ", reopening logs";
            break;
        /* 用于平滑升级 USR2 */
        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
            if (getppid() > 1 || ngx_new_binary > 0) {

                /*
                 * Ignore the signal in the new binary if its parent is
                 * not the init process, i.e. the old binary's process
                 * is still running.  Or ignore the signal in the old binary's
                 * process if the new binary's process is already running.
                 */

                action = ", ignoring";
                ignore = 1;
                break;
            }

            ngx_change_binary = 1;
            action = ", changing binary";
            break;

        case SIGALRM:
            ngx_sigalrm = 1;
            break;

        case SIGIO:
            ngx_sigio = 1;
            break;

        case SIGCHLD:
            ngx_reap = 1;
            break;
        }

        break;
    /* worker进程处理相关信号 */
    case NGX_PROCESS_WORKER:
    case NGX_PROCESS_HELPER:
        switch (signo) {

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
            if (!ngx_daemonized) {
                break;
            }
            ngx_debug_quit = 1;
            /* fall through */
        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):
            ngx_reopen = 1;
            action = ", reopening logs";
            break;
        /* worker进程忽略HUP USR2信号 */
        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
        case SIGIO:
            action = ", ignoring";
            break;
        }

        break;
    }

    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
                  "signal %d (%s) received%s", signo, sig->signame, action);

    if (ignore) {
        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
                      "the changing binary signal is ignored: "
                      "you should shutdown or terminate "
                      "before either old or new binary's process");
    }

    if (signo == SIGCHLD) {/* 表示worker进程有退出 */
        ngx_process_get_status();
    }

    ngx_set_errno(err);
}

通过中断处理函数可知,有两点不同:

1、master进程处理信号种类要比worker进程多,worker进程会忽略信号HUP,USR2。

2、给master发送信号一般都是通过kill或者nginx -s方式,然而给worker进程一般是通过master进程直接调用kill函数发送信号。 

五、总结

本篇介绍Nginx启动流程中master/worker模式,从代码中可知,master进程处理逻辑并不是很复杂。复杂的地方是在于worker进程。再下一篇将会详细介绍worker进程主函数ngx_worker_process_cycle。

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/84836850