转载 https://www.zhihu.com/question/24913599/answer/2584544572

信号是操作系统内核为我们提供用于在进程间通信的机制,内核可以利用信号来通知进程,当前系统所发生的的事件(包括关闭进程事件)。

信号在内核中并没有用特别复杂的数据结构来表示,只是用一个代号一样的数字来标识不同的信号。Linux 提供了几十种信号,分别代表不同的意义。信号之间依靠它们的值来区分

信号可以在任何时候发送给进程,进程需要为这个信号配置信号处理函数。当某个信号发生的时候,就默认执行对应的信号处理函数就可以了。这就相当于一个操作系统的应急手册,事先定义好遇到什么情况,做什么事情,提前准备好,出了事情照着做就可以了。

内核发出的信号就代表当前系统遇到了某种情况,我们需要应对的步骤就封装在对应信号的回调函数中。

信号机制引入的目的就在于:

  • 让应用进程知道当前已经发生了某个特定的事件(比如进程的关闭事件)。
  • 强制进程执行我们事先设定好的信号处理函数(比如封装优雅关闭逻辑)。

通常来说程序一旦启动就会一直运行下去,除非遇到 OOM 或者我们需要重新发布程序时会在运维脚本中调用 kill 命令关闭程序。Kill 命令从字面意思上来说是杀死进程,但是其本质是向进程发送信号,从而关闭进程。

下面我们使用 kill -l 命令查看下 kill 命令可以向进程发送哪些信号:

  

笔者这里提取几个常见的信号来简要说明下:

  • SIGINT:信号代号为 2 。比如我们在终端以非后台模式运行一个进程实例时,要想关闭它,我们可以通过 Ctrl+C 来关闭这个前台程序。这个 Ctrl+C 向进程发送的正是 SIGINT 信号。
  • SIGQUIT:信号代号为 3 。比如我们使用 Ctrl+\ 来关闭一个前台进程,此时会向进程发送 SIGQUIT 信号,与 SIGINT 信号不同的是,通过 SIGQUIT 信号终止的进程会在退出时,通过 Core Dump 将当前进程的运行状态保存在 core dump 文件里面,方便后续查看。
  • SIGKILL:信号代号为 9 。通过 kill -9 pid 命令结束进程是非常非常危险的动作,我们应该坚决制止这种关闭进程的行为,因为 SIGKILL 信号是不能被进程捕获和忽略的,只能执行内核定义的默认操作直接关闭进程。而我们的优雅关闭操作是需要通过捕获操作系统信号,从而可以在对应的信号处理函数中执行优雅关闭的动作。由于 SIGKILL 信号不能被捕获,所以优雅关闭也就无法实现。现在大家就赶快检查下自己公司生产环境的运维脚本是否是通过 kill -9 pid 命令来结束进程的,一定要避免用这种方式,因为这种方式是极其无情并且略带残忍的关闭进程行为。
  • SIGSTOP :信号代号为 19 。该信号和 SIGKILL 信号一样都是无法被应用程序忽略和捕获的。向进程发送 SIGSTOP 信号也是无法实现优雅关闭的。 通过 Ctrl+Z 来关闭一个前台进程,发送的信号就是 SIGSTOP 信号。
  • SIGTERM:信号代号为 15 。我们通常会使用 kill 命令来关闭一个后台运行的进程,kill 命令发送的默认信号就是 SIGTERM ,该信号也是本文要讨论的优雅关闭的基础,我们通常会使用 kill pid 或者 kill -15 pid 来向后台进程发送 SIGTERM 信号用以实现进程的优雅关闭。大家如果发现自己公司生产环境的运维脚本中使用的是 kill -9 pid 命令来结束进程,那么就要马上换成 kill pid 命令。

以上列举的都是我们常用的一些信号,大家也可以通过 man 7 signal 命令查看每种信号对应的含义:

应用进程对于信号的处理一般分为以下三种方式:

  • 内核定义的默认操作: 系统内核对每种信号都规定了默认操作,比如上面列表 Action 列中的 Term ,就是终止进程的意思。前边介绍的 SIGINT 信号和 SIGTERM 信号的默认操作就是 Term 。Core 的意思是 Core Dump ,即终止进程后会通过 Core Dump 将当前进程的运行状态保存在文件里面,方便我们事后进行分析问题在哪里。前边介绍的 SIGQUIT 信号默认操作就是 Core 。
  • 捕获信号:应用程序可以利用内核提供的系统调用来捕获信号,并将优雅关闭的步骤封装在对应信号的处理函数中。当向进程发送关闭信号 SIGTERM 的时候,在进程内我们可以通过捕获 SIGTERM 信号,随即就会执行我们自定义的信号处理函数。我们从而可以在信号处理函数中执行进程优雅关闭的逻辑。
  • 忽略信号:当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理,但是前边介绍的 SIGKILL 信号和 SIGSTOP 是无法被捕获和忽略的,内核会直接执行这两个信号定义的默认操作直接关闭进程。

当我们不希望信号执行内核定义的默认操作时,我们就需要在进程内捕获信号,并注册信号的回调函数来执行我们自定义的信号处理逻辑。

比如我们在本文中要讨论的优雅关闭场景,当进程接收到 SIGTERM 信号时,为了实现进程的优雅关闭,我们并不希望进程执行 SIGTERM 信号的默认操作直接关闭进程,所以我们要在进程中捕获 SIGTERM 信号,并将优雅关闭的操作步骤封装在对应的信号处理函数中。

2.1 如何捕获信号

在介绍完了内核信号的分类以及进程对于信号处理的三种方式之后,下面我们来看下如何来捕获内核信号,并在对应信号回调函数中自定义我们的处理逻辑。

内核提供了 sigaction 系统调用,来供我们捕获信号以及与相应的信号处理函数绑定起来。

int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
  • int signum:表示我们想要在进程中捕获的信号,比如本文中我们要实现优雅关闭就需要在进程中捕获 SIGTERM 信号,对应的 signum = 15 。
  • struct sigaction *act:内核中会用一个 sigaction 结构体来封装我们自定义的信号处理逻辑。
  • struct sigaction *oldact:这里是为了兼容老的信号处理函数,了解一下就可以了,和本文主线无关。

sigaction 结构体用来封装信号对应的处理函数,以及更加精细化控制信号处理的信息。

struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
.......
sigset_t sa_mask;
};
  • __sighandler_t sa_handler:其实本质上是一个函数指针,用来保存我们为信号注册的信号处理函数,优雅关闭的逻辑就封装在这里。
  • long sa_flags:为了更加精细化的控制信号处理逻辑,这个字段保存了一些控制信号处理行为的选项集合。常见的选项有:
    • SA_ONESHOT:意思是我们注册的信号处理函数,仅仅只起一次作用。响应完一次后,就设置回默认行为。
    • SA_NOMASK:表示信号处理函数在执行的过程中会被中断。比如我们进程捕获到一个感兴趣的信号,随后会执行注册的信号处理函数,但是此时进程又收到其他的信号或者和上次相同的信号,此时正在执行的信号处理函数会被中断,从而转去执行最新到来的信号处理函数。如果连续产生多个相同的信号,那么我们的信号处理函数就要做好同步,幂等等措施。
    • SA_INTERRUPT:当进程正在执行一个非常耗时的系统调用时,如果此时进程接收到了信号,那么这个系统调用将会被信号中断,进程转去执行相应的信号处理函数。那么当信号处理函数执行完时,如果这里设置了 SA_INTERRUPT ,那么系统调用将不会继续执行并且会返回一个 -EINTR 常量,告诉调用方,这个系统调用被信号中断了,怎么处理你看着办吧。
    • SA_RESTART:当系统调用被信号中断后,相应的信号处理函数执行完毕后,如果这里设置了 SA_RESTART 系统调用将会被自动重新启动。
  • sigset_t sa_mask:这个字段主要指定在信号处理函数正在运行的过程中,如果连续产生多个信号,需要屏蔽哪些信号。也就是说当进程收到屏蔽的信号时,正在进行的信号处理函数不会被中断。

屏蔽并不意味着信号一定丢失,而是暂存,这样可以使相同信号的处理函数,在进程连续接收到多个相同的信号时,可以一个一个的处理。

最终通过 sigaction 函数会调用到底层的系统调用 rt_sigaction 函数,在 rt_sigaction 中会将上边介绍的用户态 struct sigaction 结构拷贝为内核态的 k_sigaction ,然后调用 do_sigaction 函数。

最后在 do_sigaction 函数中将用户要在进程中捕获的信号以及相应的信号处理函数设置到进程描述符 task_struct 结构里。

linux信号机制(初识版)的更多相关文章

  1. 利用linux信号机制调试段错误(Segment fault)

    在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止.如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过 ...

  2. Linux信号机制

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  3. 利用linux信号机制调试段错误(Segment fault)【转】

    转自:http://blog.csdn.net/ab198604/article/details/6164517 版权声明:本文为博主原创文章,未经博主允许不得转载. 在实际开发过程中,大家可能会遇到 ...

  4. linux信号机制与python信号量

    1.信号本质 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件.在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的.信号是进程间 ...

  5. xenomai内核解析之信号signal(一)---Linux信号机制

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 1. Linux信号 1.1注册信号处理函数 ...

  6. linux信号机制 - 用户堆栈和内核堆栈的变化【转】

    转自:http://itindex.net/detail/16418-linux-%E4%BF%A1%E5%8F%B7-%E5%A0%86%E6%A0%88 此文只简单分析发送信号给用户程序后,用户堆 ...

  7. Linux信号机制代码示例

    1 基本功能: 本Blog创建了两个进程(父子进程): 父进程: 执行文本复制操作,当收到 SIGUSR1信号后,打印出现在文件复制的进度: 子进程: 每个固定时间段向父进程发送一个 SIGUSR1 ...

  8. linux 信号机制

    文章目录 1. 实时信号非实时信号 2. 信号状态: 3. 信号生命周期: 4. 信号的执行和注销 信号掩码和信号处理函数的继承 信号处理函数的继承 信号掩码的继承 sigwait 与多线程 sigw ...

  9. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  10. linux中的信号机制

    概述 Linux信号机制是在应用软件层次上对中断机制的一种模拟,信号提供了一种处理异步事件的方法,例如,终端用户输入中断键(ctrl+c),则会通过信号机制停止一个程序[1]. 这其实就是向那个程序( ...

随机推荐

  1. PageOffice在线打开 word 文件,并且禁止复制

    在线打开 word 禁用拷贝的三种方式: 1 使用 AllowCopy 属性,效果:所有的 word 进程都不能进行拷贝操作 2 禁止 word 选择功能,效果:因为无法选择,所以无法拷贝 3 使用 ...

  2. Metabase 安装和使用教程

    Metabase 是一款开源的数据分析和商业智能工具,允许企业用户在几分钟内搭建起一个功能完善的数据探索和数据分析平台,不需要编写复杂的 SQL 查询语句或者使用专业的数据可视化工具,就可以轻松地探索 ...

  3. IDS4 傻瓜式实践指南

    前言: 这是一篇实践指南,不会过多的解释原理(因为我也说不清楚,想了解的同学请移步老张的博客,里面有非常详细的介绍),本篇文章讲解如何简单的使用IDS4来实现单点登录,以及遇到的一些坑实现功能: 1. ...

  4. C# java 的 equals 与 == 的区别

    C# String a = "宜春"; String b = new String(new char[] { '宜', '春' }); String c = b; //注意这里是引 ...

  5. C#的奇技淫巧:利用WinRM来远程操控其他服务器上的进程

      前言:有时候远程服务器的进程你想偷偷去围观一下有哪些,或者对一些比较调皮的进程进行封杀,或者对一些自己研发的服务进行远程手动启动或者重启等,又不想打开远程桌面,只想悄咪咪地执行,那也许下面的文章会 ...

  6. WPF开发快速入门【2】WPF的基本特性(Style、Trigger、Template)

    概述 本文描述几个WPF的常用特性,包括:样式.触发器和控件模板. 样式/Style Style就是控件的外观,在XAML中,我们通过修改控件的属性值来设置它的样式,如: <!--直接定义sty ...

  7. Asp-Net-Core学习笔记:身份认证入门 _

    前言 过年前我又来更新了~ 我就说了最近不是在偷懒吧,其实这段时间还是有积累一些东西的,不过还没去整理-- 所以只能发以前没写完的一些笔记出来 就当做是温习一下啦 PS:之前说的红包封面我还没搞,得抓 ...

  8. 编译安装mysql5.7.20

    curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \ ...

  9. Console LDAP 配置解密

    之前通过短视频向大家介绍了 Console 如何集成 LDAP,但很多小伙伴反映按照视频里的配置后不成功.今天就结合小伙伴们反映的问题来跟大家详细介绍一下. Console LDAP 完整的配置参数如 ...

  10. 重学前端 - react-第二节: 添加ts + scss

    重学前端 - react: 添加ts + scss 简介: 上一节我们新建了 react 项目.项目中并没有使用 ts + scss. 现在我们为项目添加 ts + scss.是项目后期维护更加方便, ...