首页 > 系统相关 >linux信号机制(初识版)

linux信号机制(初识版)

时间:2022-12-05 15:47:39浏览次数:62  
标签:关闭 函数 信号处理 初识 信号 linux 进程 sigaction

转载 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,进程,sigaction
From: https://www.cnblogs.com/cuish/p/16952434.html

相关文章

  • Linux 防火墙,端口操作
    1、开放关闭端口firewall-cmd--zone=public--add-port=5672/tcp--permanent#开放5672端口firewall-cmd--zone=public--remove-port=5672/tcp--permanent#关闭56......
  • linux下动态链接库(.so)的显式调用和隐式调用
    linux下动态链接库(.so)的显式调用和隐式调用2021-12-21进入主题前,先看看两点预备知识。一、显式调用和隐式调用的区别      我们知道,动态库相比静态库的区......
  • Linux知识结构体系简述
    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。严格来讲,Linux 这个词本身只表......
  • Linux操作系统之tcpdump抓包工具详解
    前言①tcpdump工具简介:tcpdump是Linux操作系统中的字符界面的数据抓包分析软件。tcpdump可以将网络中传送的数据包完全截获下来提供分析②tcpdump是一个用于截取网络分组,并......
  • 3.2.Linux-文本过滤与处理-colrm指令:删除文件中的指定列
    1.colrnmLinuxcolrm命令用于滤掉指定的行。colrm指令从标准输入设备读取数据,转而输出到标准输出设备。如果不加任何参数,则该指令不会过滤任何一行。2.语法colrm[......
  • Linux下怎么查看MAC地址 arp
    Linux下怎么查看MAC地址https://www.yisu.com/zixun/668968.html https://blog.csdn.net/embelfe_segge/article/details/126595951linux最常用的查看mac地址的方式......
  • Linux 定时任务执行Shell脚本【CentOS6.8】
    本人一直从事Windows系统的开发,最近一年基本上都在研究.NET跨平台相关的知识,最近遇到一个棘手的问题,由于前期技术选型的时候说采用WebAPI的方式进行部署,我当时就答应了,到最......
  • Linux将一个文件夹或文件夹下的所有内容复制或移动到另一个文件夹
    1、将一个文件夹下的所有内容复制到另一个文件夹下cp-r/home/packageA/*/home/cp/packageB/或cp-r/home/packageA/./home/cp/packageB/这两种方法效果是一样的。方法......
  • Linux系统zabbix_agentd客户端安装与配置
    官网下载zabbix安装包(zabbix安装包里包含了zabbix_agentd客户端安装包,我们只选择zabbix_agentd客户端安装) zbbix官网下载地址:  http://www.zabbix.com/download ......
  • RT-Thread的信号 在keil中使用程序框架
    按照如下方式,添加信号,使用信号信号量的原理:操作系统中的信号量在解决线程之间的同步中起着非常大的作用,那么什么是信号量呢?百度百科:信号量(Semaphore),有时被称为信号灯,......