Nginx源码分析--进程间通信--Signal篇

16 篇文章 6 订阅
1 篇文章 0 订阅

Nginx进程间通信–Signal篇

Nginx使用信号来管理master进程和worker进程。

信号Signal的概念

信号是软件中断。很多比较重要的应用程序都需要处理信号。信号提供了一种处理异步事件的方法。
当某个信号出现时,可以告诉内核按下列3种方式之一进行处理:
1、忽略信号。有两种信号不能被忽略,它们是SIGKILL和SIGSTOP,原因是它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号,则进程的运行行为是未定义的。
2、捕捉信号。通知内核在某种信号发生时,调用一个用户函数,可执行用户希望对这种事件进行的处理。自定义的信号处理函数必须是可重入的,即异步信号安全的。SIGKILL和SIGSTOP不能被捕捉。
3、执行系统默认动作。大多数信号的系统默认动作是终止该进程。
注:SIGKILL和SIGSTOP既不能被忽略,也不能被捕捉。

Nginx进程通信中Signal的使用

1、通过Signal发送控制信号
/home/nginx/sbin/nginx  -h
nginx version: nginx/1.16.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -T            : test configuration, dump it and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload

当我们向已经启动的nginx服务发送指令的时候,是通过发送signal实现的。

ngx_signal==1
main
ngx_get_options
ngx_signal_process
ngx_os_signal_process
kill发送signo
2、监控worker

Nginx的Master在监控到worker异常终止时,会自动拉起新的worker,其原理就是通过信号实现的。
Nginx的Master进程的自定义信号处理函数中,当受到SIGCHLD信号时,会将ngx_reap = 1,在Master的主进程循环中,会调用ngx_reap_children,在ngx_reap_children中会判断该worker进程满足条件(非exiting,非terminate,非quit状态),创建新的worker。

Nginx进程通信Signal的源码分析

Nginx定义ngx_signal_t,用于描述接收到信号时的行为。

typedef struct {
    //信号值
    int     signo;
    //信号名称
    char   *signame;
    //nginx对应的名称,例如reload,reopen,stop等
    char   *name;
    //nginx自定义信号处理函数
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;

自定义的信号处理函数:

Master处理信号全局变量值
NGX_SHUTDOWN_SIGNALngx_quit = 1
NGX_TERMINATE_SIGNALngx_terminate = 1
NGX_NOACCEPT_SIGNALngx_noaccept = 1
NGX_RECONFIGURE_SIGNALngx_reconfigure = 1
NGX_REOPEN_SIGNALngx_reopen = 1
NGX_CHANGEBIN_SIGNALngx_change_binary = 1
SIGALRMngx_sigalrm = 1
SIGIOngx_sigio = 1
SIGCHLDngx_reap = 1
Worker处理信号全局变量值
NGX_NOACCEPT_SIGNALngx_quit = 1
NGX_SHUTDOWN_SIGNALngx_quit = 1
NGX_TERMINATE_SIGNALngx_terminate = 1
SIGINTngx_terminate = 1
NGX_REOPEN_SIGNALngx_reopen = 1
NGX_RECONFIGURE_SIGNALignoring
NGX_CHANGEBIN_SIGNALignoring
SIGIOignoring

一、首先在main函数中调用ngx_init_signals,初始化所有需要自定义处理的信号。

ngx_int_t
ngx_init_signals(ngx_log_t *log)
{
......
    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));

        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;

        } else {
            sa.sa_handler = SIG_IGN;
        }
        //信号集初始化为空
        sigemptyset(&sa.sa_mask);
        //修改信号的处理函数
        if (sigaction(sig->signo, &sa, NULL) == -1) {
            ......
        }
    }
......
    return NGX_OK;
}

二、在master守护进程将所有信号添加到信号集里面。

void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
......
    //清空信号集set,然后将需要自定义处理的信号添加到信号集中
    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));
    //屏蔽信号集set中的信号
    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }
......
    sigemptyset(&set);

    for ( ;; ) {
        ......
        //重新设置信号集set,并挂起当前进程
        sigsuspend(&set);
        ......
    }
......
}

三、worker进程将所有信号添加到信号集里面。


static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
    sigemptyset(&set);

    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }

}

四、master向worker发送信号:

static void
ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
{
#if (NGX_BROKEN_SCM_RIGHTS)

    ch.command = 0;

#else

    switch (signo) {

    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
        ch.command = NGX_CMD_QUIT;
        break;

    case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        ch.command = NGX_CMD_TERMINATE;
        break;

    case ngx_signal_value(NGX_REOPEN_SIGNAL):
        ch.command = NGX_CMD_REOPEN;
        break;

    default:
        ch.command = 0;
    }

#endif

    ch.fd = -1;


    for (i = 0; i < ngx_last_process; i++) {
        //detached=1,表示nginx平滑升级的进程,没有亲属关系的进程。
        if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {
            continue;
        }
        //just_spawn,表示刚创建的子进程。
        if (ngx_processes[i].just_spawn) {
            ngx_processes[i].just_spawn = 0;
            continue;
        }
        //正在退出的进程
        if (ngx_processes[i].exiting
            && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))
        {
            continue;
        }

        if (ch.command) {
            //如果能通过channel发送成功,则进入下一个循环
            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;
                }

                continue;
            }
        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                       "kill (%P, %d)", ngx_processes[i].pid, signo);
        //直接通过kill函数向worker进程号发送信号
        if (kill(ngx_processes[i].pid, signo) == -1) {
            err = ngx_errno;
            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
                          "kill(%P, %d) failed", ngx_processes[i].pid, signo);

            if (err == NGX_ESRCH) {
                ngx_processes[i].exited = 1;
                ngx_processes[i].exiting = 0;
                ngx_reap = 1;
            }

            continue;
        }

        if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
            ngx_processes[i].exiting = 1;
        }
    }
......
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值