深入理解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);
#elsengx_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信号的时候,需要分几步:

    1. 初始化一个sigaction结构体。
    1. 设置sigaction结构体中sa_sigaction或者sa_handler(二选一)至信号处理函数。对于前者,需要设置sa_flags = SA_SIGINFO。
    1. 如果不希望在处理当前signal的时候block其他信号,那么用sigemptyset清空sa_mask。
    1. 最后,通过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));}

 &esmp;首先它判断是否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进程执行相应的动作的呢?

  这里就涉及到跨进程信号发送的操作了。
&esmp; 新启动的进程根据命令行参数,会读取正在提供服务的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机制来实现对进程的各种控制功能,有不当之处敬请指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/9066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024中国植物资源化妆品创新展在国家植物园成功举办

2024中国植物资源化妆品创新展(简称国植美妆展)于今年05月06日在北京国家植物园圆满落下帷幕。国植美妆展由中国广告协会化妆品工作委员会与中国抗衰老促进会化妆品产业分会指导,北京华晟德观文化科技发展有限公司主办,于03月30日…

安卓模拟器访问主机局域网

误打误撞能够访问主机局域网了 但是不太懂是因为哪一部分成功的 先记录一下 PC:mac系统 安卓编译器:Android Studio 步骤 只需要在PC上进行设置 1. 在【设置】中,打开已连接的Wi-Fi的【详细信息】 2. TCP/IP --> 配置IPv6,修…

前端组件库之ant-design-vue

在这里记录一个这个组件库我之前没有发现最近才发现的一个很好用的功能(应该叫功能吧?) 就是 这个flex弹性布局,之前在开发时,一直使用elementUI,是第一次使用这个组件库,所以没有发现这个功能这么好用 你…

第21天 反射

反射概述 想象一下,你在一个房间里边,但你看不见自己,也不知道自己是谁。这时候你面前有一个镜子,你可以通过镜子的反射来观察自己。反射就像这面镜子。它让你能够检查、分析、修改Java中的对象、类、方法等 使用情况&#xff1…

WPF控件之StackPanel布局控件

StackPanel别名堆栈panel 使其子元素按照一定方式进行布局&#xff0c;子元素排布方式要么设置为水平排布&#xff0c;要么垂直排布。 属性 Orientation设置排列方式(默认的是垂直排布) : Horizontal水平排布 Vertical 垂直排布 实例 <StackPanel Orientation"Vert…

视频号小店怎么做?店铺运营详细步骤讲解,全网独家

大家好&#xff0c;我是电商笨笨熊 视频号小店作为今年的电商黑马&#xff0c;下一个站在风口的项目&#xff0c;自是吸引了不少的玩家&#xff1b; 先不说视频号自身庞大的流量体系&#xff0c;单单是高客单的市场就值得尝试一把&#xff1b; 且当前视频号小店刚刚推出不久…

智慧文旅赋能旅游服务升级:以科技创新驱动行业变革,打造智慧化、个性化、高效化的旅游新体验,满足游客日益增长的多元化需求

目录 一、引言 二、智慧文旅的概念与内涵 三、智慧文旅在旅游服务升级中的应用 1、智慧旅游服务平台建设 2、智慧景区管理 3、智慧旅游营销 四、智慧文旅推动旅游行业变革的案例分析 案例一&#xff1a;某智慧旅游城市建设项目 案例二&#xff1a;某景区智慧化改造项目…

SpringBoot如何实现动态数据源?

在Spring Boot中实现动态数据源主要涉及到创建和管理不同的数据源&#xff0c;并在运行时根据需要切换。这可以通过编程方式配置Spring的AbstractRoutingDataSource来完成。下面我会逐步介绍如何实现动态数据源&#xff0c;并给出代码示例。 第1步&#xff1a;添加依赖 首先&…

学习Python的第3天:深入理解数据类型与控制结构

经过前两天的Python学习&#xff0c;我们已经对Python的基本语法和简单程序有了初步的了解。今天&#xff0c;我们将进一步深入Python的世界&#xff0c;探索数据类型和控制结构这两个核心概念。 1. 数据类型 Python是一种动态类型语言&#xff0c;这意味着我们不需要在声明变…

深度学习学习日记5.8

随着patch size的增大&#xff0c;图像的特征图分辨率会相应减小。这是因为更大的patch size意味着模型在每一步处理的区域更大&#xff0c;因此产生的特征图会更为粗糙。 具体来说&#xff1a; 较大的patch size会导致模型在每个步骤中将更大的区域视为一个单元&#xff0c;…

算法系列之快速排序最佳实践你知道吗

1.原理 快速排序采用"分而治之递归排序"的思想&#xff0c;对于一组数据&#xff0c;选择一个基准元素&#xff08;base&#xff09;&#xff0c;比如选择第一个、中间位置元素或最后一个元素&#xff0c;通过第一轮扫描&#xff0c;比base小的元素都在base左边&…

Redis简介和数据结构

目录 简介 进入之后身份认证才能使用 优点 用途&#xff1a; 数据结构 string string自动扩容 Redis中的简单动态字符串&#xff08;SDS&#xff09;具有以下优点&#xff1a; SDS数据的编码格式 比较&#xff1a; string 常用操作 分布式锁 使用情况&#xff0c;…

Dark Reader:夜间模式,启动!

名人说&#xff1a;一点浩然气&#xff0c;千里快哉风。 ——苏轼 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、介绍二、下载安装1、Chrome应用商店&#xff08;需科学&#xff09;2、第三方直链下载 三、使…

Pygame简单入门教程(绘制Rect、控制移动、碰撞检测、Github项目源代码)

Pygame简明教程 引言&#xff1a;本教程中的源码已上传个人Github: GItHub链接 视频教程推荐&#xff1a;YouTube教程–有点过于简单了 官方文档推荐&#xff1a;虽然写的一般&#xff0c;但还是推荐&#xff01; Navigator~ Pygame简明教程安装pygame一、代码框架二、案件输入…

五一开始内卷前端,如何迅速的一个月内找到工作!

写在前面 五一过了代表新的一年不知不觉过了半年了&#xff0c;各位工作找到怎么样&#xff0c;有没有在工作中遇到解决不了的问题&#xff0c;这些问题后面怎么处理了呢&#xff1f; hello大家好&#xff0c;我又又又来了&#xff0c;今天纯干货&#xff0c;上班的朋友适当摸…

video.js的请求头问题

为了防止视频被轻易下载&#xff0c;我们项目需要在请求视频地址的时候&#xff0c;增加token识别&#xff0c;避免url一粘贴到浏览器地址上就能被盗。 明明一开始就找到的方法&#xff1a; // ts-ignorevideojs.Vhs.xhr.beforeRequest function (options) {options.headers …

Linux下的I2C通信

I2C通信: 一.硬件初识: IIC(inter-intergrated-Circu):内部集成总线 四线通讯:SCL,SDA,GND,VCC,串行,半双工 I2C 总线是同步,串行,半双工通信总线。 I2C 总线由时钟线 SDA 和 SCL 两根信号线构成。并且都有上拉电阻。确保总线空闲状态为高电平。 I2C 总线支持多…

[运维] GitLab-Runner is failing jobs with a “shell not found“ error

参考文献 jobs-fails-shell-not-found 处理方式 image:name: node:18-busterentrypoint: [ /bin/bash, -c, ln -snf /bin/bash /bin/sh && /bin/bash -c $0 ]

ElasticSearch 与 OpenSearch:拉开性能差距

Elasticsearch 与 OpenSearch&#xff1a;扩大性能差距 对于任何依赖快速、准确搜索数据的组织来说&#xff0c;强大、快速且高效的搜索引擎是至关重要的元素。对于开发人员和架构师来说&#xff0c;选择正确的搜索平台可以极大地影响您的组织提供快速且相关结果的能力。在我们…

Leetcode 116:填充每一个节点的下一个右侧节点指针

给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到…