一文了解nginx中的signal处理机制

 更新时间:2024年05月31日 09:21:50   作者:码农心语  
nginx利用信号处理机制,可以捕获和处理各种信号,本文主要介绍了nginx中的signal处理机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1. 引言

在计算机系统中,信号处理是一项重要的任务,它允许操作系统和应用程序之间进行通信和协调。在网络服务器软件中,如Nginx,信号处理机制起着关键作用,它能够捕获和处理各种类型的信号,从而实现服务器的灵活控制和运行时的动态行为。

nginx是一款高性能、轻量级的Web服务器和反向代理服务器,被广泛应用于构建可靠、高效的Web应用程序和服务。为了满足各种需求和应对不同的运行时情况,nginx提供了丰富的信号处理机制,使得管理员和开发人员能够通过发送信号来实现对服务器的管理和控制。

信号是一种在操作系统中用于通知进程发生某种事件或请求某种操作的机制。它可以用于向进程发送中断信号、终止信号、重启信号等,以及自定义的应用程序信号。nginx利用信号处理机制,可以捕获和处理各种信号,例如重新加载配置文件、优雅地停止或重启服务器等。

深入理解nginx中的信号处理机制需要了解信号的基本概念和操作系统对信号的支持。当nginx接收到一个信号时,它会根据信号的类型和当前的运行状态执行相应的操作。例如,当接收到重新加载配置文件的信号时,nginx会重新读取配置文件并应用新的配置,而不需要重启整个服务器。

2. signal信号处理函数的注册

在nginx的main函数中有一个函数调用,如下:

    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }

这个调用的作用就是向操作系统注册当前进程的signal处理函数。

下面是ngx_init_signals函数的实现源码:

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

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

在ngx_init_signals函数中,对定义的signals数组进行遍历,并将对应的signal处理函数注册到操作系统中。

在注册一个signal信号的时候,需要分几步:

  • 初始化一个sigaction结构体。

  • 设置sigaction结构体中sa_sigaction或者sa_handler(二选一)至信号处理函数。对于前者,需要设置sa_flags = SA_SIGINFO。

  • 如果不希望在处理当前signal的时候block其他信号,那么用sigemptyset清空sa_mask。

  • 最后,通过sigaction向操作系统注册消息处理函数。

通过上面的循环遍历,nginx注册了SIGHUP(reload)、SIGUSR1(reopen)、SIGWINCH(noaccept)、SIGTERM(stop)、SIGQUIT(quit)、SIGUSR2(change bin)、SIGARLRM(timer)、SIGINT(stop)、SIGIO()、SIGCHLD(child reap)、SIGSYS(ignore)、SIGPIPE(ignore)共12个信号。

signals的定义如下:

ngx_signal_t  signals[] = {
    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      "reload",
      ngx_signal_handler },
-
    { ngx_signal_value(NGX_REOPEN_SIGNAL),
      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
      "reopen",
      ngx_signal_handler },

    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
      "",
      ngx_signal_handler },

    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
      "stop",
      ngx_signal_handler },

    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
      "quit",
      ngx_signal_handler },

    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
      "",
      ngx_signal_handler },

    { SIGALRM, "SIGALRM", "", ngx_signal_handler },

    { SIGINT, "SIGINT", "", ngx_signal_handler },

    { SIGIO, "SIGIO", "", ngx_signal_handler },

    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },

    { SIGSYS, "SIGSYS, SIG_IGN", "", NULL },

    { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },

    { 0, NULL, "", NULL }
};

3. 设置信号阻塞

为了nginx在处理信号的过程中确保能够正确地处理并且避免被中断,需要对信号设置block阻塞标记,从而能够在处理指定信号的时候,避免新的信号进来打扰处理过程。

在ngx_master_process_cycle函数(当配置开启了master_process模式时会作用master进程的主循环)的开头部分,进行了相关设置,源码如下:

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

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

    sigemptyset(&set);


这里对前面设置的10个信号(除了SIG_IGN)进行了设置。

4. signal信号的处理

在nginx中,signal信号是由ngx_signal_handler函数负责接收处理的。不过,ngx_signal_handler函数对信号的处理其实就是对应接收到的信号设置相应的标记,然后立即返回。譬如收到SIGTERM信号,则设置ngx_terminate = 1,收到SIGHUP信号,则设置ngx_reconfigure等等。其自己本身不进行实际的信号处理。

signal信号的处理逻辑是在主循环中进行的。如果是master/worker多进程运行模式下,在ngx_master_process_cycle函数中处理,如果是单进程运行模式下,则是在ngx_single_process_cycle函数中进行处理。在主循环函数中,它会检查ngx_signal_handler中设置的标记位,然后根据各个标记位进行对应的处理。

譬如在ngx_master_process_cycle函数中对配置重加载信号的处理逻辑如下:

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

首先它判断是否ngx_reconfigure被设置为1了,如果没有设置,那么不执行配置重加载的操作。

接着,如果是正在更新二进制文件操作,即ngx_new_binary=1,那么需要在这里启动新的worker进程和cache manager进程。

再下来是调用ngx_init_cycle重新加载配置文件。

加载新的worker进程,最后通知老的worker进程进行优雅退出。

5. 跨进程发送signal

在nginx运行的过程中,如果我们需要让当前的nginx能够重新加载配置文件,我们可以在命令行输入以下命令:

nginx -s reload

又或者,如果我们希望停止nginx运行,我们可以在命令行输入一下命令:

nginx -s stop

因为我们在命令行输入以上命令的时候,其实shell又重新启动了一个新的nginx进程,那新的nginx进程是如何通知正在提供服务的nginx进程执行相应的动作的呢?

这里就涉及到跨进程信号发送的操作了。

新启动的进程根据命令行参数,会读取正在提供服务的nginx进程的pid文件,得到它的master进程的pid,然后调用系统函数kill来向master进程,这样子master进程就会收到对应的信号,然后master主循环函数就会进行信号的处理。

在main函数中,我们可以看到下面的代码:

    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }

意思就是向nginx进程发送指定的信号。再看ngx_signal_process函数的实现:

ngx_int_t
ngx_signal_process(ngx_cycle_t *cycle, char *sig)
{
    ssize_t           n;
    ngx_pid_t         pid;
    ngx_file_t        file;
    ngx_core_conf_t  *ccf;
    u_char            buf[NGX_INT64_LEN + 2];

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = ccf->pid;
    file.log = cycle->log;

    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);

    if (file.fd == NGX_INVALID_FILE) {
        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
                      ngx_open_file_n " \"%s\" failed", file.name.data);
        return 1;
    }

    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", file.name.data);
    }

    if (n == NGX_ERROR) {
        return 1;
    }

    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }

    pid = ngx_atoi(buf, ++n);

    if (pid == (ngx_pid_t) NGX_ERROR) {
        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
                      "invalid PID number \"%*s\" in \"%s\"",
                      n, buf, file.name.data);
        return 1;
    }

    return ngx_os_signal_process(cycle, sig, pid);

}

非常好理解,就是读取pid文件,然后调用ngx_os_signal_process函数对pid发送signal。由于linux/unix和windows的signal机制是不一样的,所以ngx_os_signal_process函数针对两类操作系统nginx进行了单独实现,这里不再赘述。

6. 总结

以上通过对nginx的源码分析,从signal信号的注册和阻塞状态设置,到signal信号的处理,最后到跨进程singla信号的发送进行了详细的介绍,我们可以从中一窥nginx如何利用操作系统的signal机制来实现对进程的各种控制功能。

到此这篇关于一文了解nginx中的signal处理机制的文章就介绍到这了,更多相关nginx signal 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Nginx配置请求头携带原始请求信息的实现

    Nginx配置请求头携带原始请求信息的实现

    本文主要介绍了Nginx配置请求头携带原始请求信息的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • windows下nginx如何操作命令

    windows下nginx如何操作命令

    这篇文章主要介绍了windows下nginx如何操作命令,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Nginx本地目录映射实现代码实例

    Nginx本地目录映射实现代码实例

    这篇文章主要介绍了Nginx本地目录映射实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • nginx部署vue项目的详细图文教程

    nginx部署vue项目的详细图文教程

    很多小伙伴在做完Vue项目之后,想要部署到服务器上自己运行试试,下面这篇文章主要给大家介绍了关于nginx部署vue项目的详细图文教程,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 配置nginx保证frps服务器与web共用80端口的方法

    配置nginx保证frps服务器与web共用80端口的方法

    这篇文章主要介绍了frps服务端与nginx可共用80端口的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • 一文弄懂Nginx的location匹配的实现

    一文弄懂Nginx的location匹配的实现

    这篇文章主要介绍了一文弄懂Nginx的location匹配的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • nginx共享内存的机制详解

    nginx共享内存的机制详解

    在nginx的进程模型下,类似流量统计、流量控制、数据共享、等需要多个工作进程共同配合完成任务,共享内存是一个重要的进程通讯的方案,本文主要介绍了nginx共享内存的机制详解,感兴趣的可以了解一下
    2022-03-03
  • Nginx搭载负载均衡及前端项目部署

    Nginx搭载负载均衡及前端项目部署

    本文介绍了如何使用Nginx实现负载均衡和前端项目部署,通过配置Nginx的负载均衡功能,可以有效地分发客户端请求,提高服务器的处理能力,感兴趣的可以了解一下
    2023-11-11
  • nginx共享内存机制详解

    nginx共享内存机制详解

    这篇文章主要介绍了nginx共享内存机制详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • nginx 负载均衡轮询方式配置详解

    nginx 负载均衡轮询方式配置详解

    负载均衡(load-balance)就是将负载分摊到多个操作单元上执行,从而提高服务的可用性和响应速度,带给用户更好的体验,本文给大家介绍nginx 负载均衡轮询方式配置,感兴趣的朋友一起看看吧
    2022-03-03

最新评论