【Linux】信号(signal)

目录

一、信号概念:

二、信号的常见状态:

信号递达:

信号未决:

阻塞信号:

忽略信号:

信号在内核中的表示:

三、信号相关函数:

sigset_t (类型):

信号集操作函数:

signal() 函数:

sigpending() 函数:

sigaction() 函数:

sigprocmask() 函数:

四、补充:SIGCHLD信号


一、信号概念:

在Linux系统中,信号(signal)是一种软件中断,它提供了一种机制来通知进程发生了某个事件。当系统检测到某种异常或特殊事件时,会向相关的进程发送一个信号。进程可以指定对每种信号的处理方式,包括忽略信号、捕捉信号、以及执行默认操作。

在Linux终端中我们可以通过 kill -l 来查询所有的信号

每一个信号都有一个名字和编号,都以SIG开头,其实本质上就是一个宏,例如2号信号其实就是:#define SIGINT 2          这些信号被定义在 signal.h 文件中。

非实时信号:(1 - 31号信号),也叫不可靠信号:

        它们的主要特点是不支持排队,即如果发送多次相同的信号,进程可能只能收到一次,信号可能会丢失。

实时信号:(32 - 64号信号),也叫可靠信号:

        它们的主要特点是支持排队,即如果发送多次相同的实时信号,进程可以收到多次,信号不会丢失。(一般用来实现更复杂的进程间通信和同步机制)

所以这篇主要围绕非实时信号做文章:

Core Dump(核心转储)

概念:

在OS中,当一个程序异常终止时(例如内存非法访问导致的段错误),OS会将此时进程地址空间的内容以及有关进程状态的其他信息写成一个磁盘文件(文件名一般为core)。这就是核心转储。

在事后可以通过调试器检查croe文件弄清错误原因,但是OS设置默认是不允许产生croe文件的,因为其中可能包含用户的一些隐私信息,不安全,可以通过ulimit命令修改成允许产生croe文件

可以在终端输入 ulimit -c 命令,如果结果为0,就是默认关闭croe dump,就算程序异常终止也不会产生croe文件。

可以通过ulimit -c unlimited开启croe dump功能,不限制croe文件的大小,如果要限制,就将unlimited改成你想要的文件大小(KB)即可,但注意,该命令的生命周期只在当前终端有效。

二、信号的常见状态:

信号递达:

信号递达就是指当信号被发送和接收后,信号的处理过程,当一个信号被发送到一个进程时,需要经过多个步骤才能被接收并处理。

例如给进程A发送系统调用kill():

1:发送信号:OS产生信号,准备发送到进程A。

2:信号递达:进程A收到信号的过程。

2:信号处理:进程A收到信号,并根据信号的类型和处理方式来进行对应的处理

注意:

        在信号递达和信号处理的过程中,可能会发生信号丢失或者信号被阻塞的情况,当一个进程处于阻塞状态时,它无法收到任何信号,直到解除阻塞状态,如果有多个信号同时到达进程时会出现信号排队的情况,此时进程会按照一定的规则来处理这些信号。

信号未决:

信号未决(Pending Signal)指的是一个进程接收到但尚未处理的信号。当一个信号被发送给一个进程时,如果该进程当前正在执行某个信号处理函数或者该信号已经处于未决状态,则该信号会被放入进程的信号未决位集(Pending Signal Mask,简称pending表)中,等待进程从信号处理函数返回后进行处理。

在pending表中,每个位代表一个信号默认为0,如果该位为1,则表示该位对应的信号处于未决状态。一个进程可以通过sigpending()系统调用来查询自己的信号未决位集。

如果一个进程接收到多个同类型的信号并且信号处理函数尚未返回,则这些信号将被合并成一个信号,并只记录一次信号未决。

当进程解除信号阻塞状态后,它必须处理所有未决的信号,否则这些信号将继续被保留在信号未决位集中,可能会导致信号丢失或者其他问题。所以要注意及时处理所有未决信号,避免信号积压导致系统异常。

阻塞信号:

阻塞信号是指进程可以选择暂时延迟处理某些特定信号的传递和处理。在Linux中,进程可以通过设置信号阻塞掩码(Signal Mask)来达到这一目的。

当一个信号被发送给一个进程时,内核会首先检查该信号是否在进程的信号阻塞掩码中。如果信号在阻塞掩码中,则该信号将被暂时挂起,直到该信号从阻塞状态解除后才能被处理。可以使用sigprocmask()系统调用来修改信号阻塞掩码。

当进程解除对某个信号的阻塞时,如果有多个该类型的信号在阻塞期间到达,那么这些信号将按照某种规则进行排队,等待进程逐个处理。另外,即使信号被阻塞,但仍然会记录在信号未决位集中,等待进程除阻塞后处理。

忽略信号:

忽略信号是指进程可以选择不对某些特定信号进行处理,即忽略该信号的传递和默认处理行为。在Linux中,进程可以通过设置信号处理函数为SIG IGN来达到这一目的。

当一个信号被发送给一个进程时,内核会首先检查该信号的处理方式。如果进程将该信号的处理函数设置为SIGIGN(忽略信号),则内核将不对该信号进行任何处理,直接丢弃该信号。

但是并不是所有的信号都可以被忽略或捕捉的。如SIGKILL和SIGSTOP,它们具有固定的处理行为。防止用户特意屏蔽所有信号导致进程无法退出

信号在内核中的表示:

示意图:

如图:

SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作(SIG_DFL)。

SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler()。

 

三、信号相关函数:

sigset_t (类型):

sigset_t是一个数据类型,用于表示一个信号集,他是由一个位图组成的数据结构,用于跟踪和管理多个信号的状态,通常以无符号整数或者数组的形式实现

在 Linux 中,sigset_t 是通过使用位操作来表示信号集的。每个信号对应 sigset_t中的一个位(bit),如果某个位被设置为 1,则表示相应的信号在该信号集中被包含;如果某个位被设置为0,则表示相应的信号在该信号集中不被包含。

信号集操作函数:

上述函数都有相同的返回值:成功返回 0,失败返回 -1,并设置相应的错误码(errno)

signal() 函数:

signal() 函数是一个用于信号处理的POSIX标准C函数,该函数的主要作用是向操作系统注册一个信号处理函数,用于在特定信号发生时执行相应的处理逻辑。

#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
  • sig:指定要处理的信号编号。
  • func:指向一个函数的指针,该函数在信号发生时被调用,其返回类型为void,参数为int类型,表示接收到的信号编号。
  • 返回值:返回之前对 sig 信号的处理函数指针,如果没有则返回SIG_ERR,并设置errno。

代码演示:(使用signal函数捕捉SIG_INT,并设置handler函数自定义处理)

#include <iostream>
#include <unistd.h> // getpid
#include <signal.h> // signal函数头文件void handler(int signum)
{std::cout << "signum: " << signum << std::endl;
}int main()
{// 捕捉2号信号并进行自定义函数处理if (signal(SIGINT, handler) == SIG_ERR){perror("signal error!");exit(1);}std::cout << "pid: " << getpid() << std::endl;while (true){sleep(1);}return 0;
}

sigpending() 函数:

sigpending() 函数是Linux系统中的一个系统调用,用于查询当前进程的未决信号集合。

未决信号集和:指已经发送给进程但尚未被处理的信号。这些信号可能因为被屏蔽而无法立即传递给进程,但会停留在待处理状态,等待进程解除对它们的阻塞时进行处理。

#include <signal.h>  
int sigpending(sigset_t *set);

set:指向一个sigset_t类型的信号集合的指针,用于存储当前进程的未决信号集合。调用成功后,该信号集合将包含所有未决信号的集合。

返回值:成功返回 0,失败返回 -1。

该函数可以在下两个函数中用到,这里就将代码融入到下面。

sigaction() 函数:

sigaction()函数UNIX 和类 UNIX 系统(如 Linux)中用于检查或改变与信号相关联的处理动作的函数。该函数允许程序捕获由系统产生的信号,并定义这些信号的处理方式(如忽略、终止程序或执行用户定义的函数等)。

#include <signal.h>  
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
  • sig指定要操作的信号的编号。这是一个整型值,对应于不同的信号(如 SIGINT 表示中断信号, SIGTERM 表示终止请求等)。
  • act指向 struct sigaction 结构的指针,该结构定义了新的信号处理动作。如果此参数为 NULL,则不会改变信号的处理方式。
  • oact如果非 NULL,则指向的 struct sigaction 结构会被填充为该信号当前的处理方式。
  • 返回值:成功时返回 0;失败时返回 -1 并设置 errno。

 

struct sigaction 结构:

struct sigaction {  void     (*sa_handler)(int);  void     (*sa_sigaction)(int, siginfo_t *, void *);  sigset_t   sa_mask;  int        sa_flags;  void     (*sa_restorer)(void);  // 历史遗留字段,可直接忽略。
};
  • sa_handler一个函数指针,指向信号处理函数。当信号发生时,会调用这个函数。如果设置为 SIG_IGN,则忽略该信号;如果设置为 SIG_DFL,则使用信号的默认处理方式。
  • sa_sigaction与 sa_handler 类似,但它提供了更多的上下文信息,包括信号的 siginfo_t 结构和上下文指针(ucontext_t)。要使用 sa_sigaction,sa_flags 必须包含 SA_SIGINFO 标志。
  • sa_mask在信号处理函数执行期间,系统会阻塞此信号集中包含的信号。这有助于防止在信号处理函数执行期间发生信号重入问题。
  • sa_flags控制信号处理的各种选项。常见的选项包括 SA_RESTART(如果信号处理函数返回后,被中断的系统调用会自动重启),SA_NODEFER(防止信号被自动阻塞),以及 SA_SIGINFO(使用 sa_sigaction 而不是 sa_handler)。

 

代码演示:(使用sigaction函数捕捉SIG_INT,并设置handler函数自定义处理,打印pindeng表检测2号信号进入信号未决状态。)

#include <iostream>
#include <signal.h>
#include <unistd.h> // getpidvoid Print(sigset_t &pending)
{for (int sig = 31; sig > 0; sig--){if (sigismember(&pending, sig)) // 检测特定对应的信号是否在pending集中{std::cout << 1;}else{std::cout << 0;}}std::cout << std::endl;
}int main() {sigset_t newset, oldset;// 设置要阻塞的信号集sigemptyset(&newset);sigaddset(&newset, SIGINT);// 阻塞 SIGINT 信号if (sigprocmask(SIG_BLOCK, &newset, &oldset) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is blocked. Press Ctrl+C to send the signal.\n");// 挂起进程,等待信号到达while(true){sigset_t pending;sigpending(&pending);Print(pending);sleep(2);}// 恢复原来的信号屏蔽字if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is unblocked. Signal handling resumed.\n");return 0;
}

sigprocmask() 函数:

sigprocmask() 函数用于修改进程的信号屏蔽字集合,进而控制哪些信号可以被进程接收和处理。

#include <signal.h>  
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:指定对信号屏蔽字的操作方式,可以是以下三个值之一:
    • SIG_BLOCK:将set指向的信号集合添加到当前进程的信号屏蔽字中。
    • SIG_UNBLOCK:从当前进程的信号屏蔽字中移除set指向的信号集合。
    • SIG_SETMASK:将当前进程的信号屏蔽字设置为set指向的信号集合。
  • set:指向一个信号集合的指针,用于指定需要修改的信号集合。根据how参数的不同,这个集合的意义也会有所不同。
  • oldset:是一个可选的输出参数,用于获取调用sigprocmask函数前的旧的信号屏蔽字。如果不需要获取旧的信号屏蔽字,可以将其设置为NULL。
  • 返回值:成功返回 0,失败返回 -1,并设置errno。

 

代码演示:(使用sigprocmask()函数屏蔽SIG_INT信号,并打印pindeng表检测2号信号进入信号未决状态。)

#include <iostream>
#include <signal.h>
#include <unistd.h> // getpidvoid Print(sigset_t &pending)
{for (int sig = 31; sig > 0; sig--){if (sigismember(&pending, sig)) // 检测特定对应的信号是否在pending集中{std::cout << 1;}else{std::cout << 0;}}std::cout << std::endl;
}int main()
{sigset_t newset, oldset;// 清空信号集sigemptyset(&newset);// 添加2号信号sigaddset(&newset, SIGINT);// 屏蔽信号集if (sigprocmask(SIG_BLOCK, &newset, &oldset) == -1){perror("sigprocmask");return 1;}std::cout << "Signal No.2 successfully blocked!" << "\n"<< "pid: " << getpid() << std::endl;// 挂起进程,等待信号到达while (true){sleep(2);sigset_t pending;sigpending(&pending);Print(pending);}// 恢复原来的信号屏蔽字if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1){perror("sigprocmask");return 1;}return 0;
}

四、补充:SIGCHLD信号

这里简单补充一下SIGCHLD信号:

我们都知道在子进程结束前,父进程要等待子进程的结束并进行资源回收,否则会产生僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。

采用第一种方式,父进程阻塞了就不能处理自己的工作了;

采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。

其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可

简单实现的话,代码形式如下:

​​​​​​​

执行图:

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

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

相关文章

二、QGroundControl开发环境搭建

文章目录 环境列表QGC源码下载编译 环境列表 QGC GithubPX4-AutopilotQt 5.15Ubuntu20.04 QGC源码下载编译 官网下载指令 如下 // Clone the repo (or your fork) including submodules: git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git // Upda…

Axure中继器实战篇:让数据展示和交互设计更上一层楼!

Axure中继器实战篇:让数据展示和交互设计更上一层楼! 前言 经过了前两章的学习,接下来我们去模拟的实际场景开启实战篇,以下是界面 1.前期准备 前期把页面准备好后,给中继器的每个单元格命名为了方便数据绑定的操作。 为了演示我准备了几十行数据,建议也多准备一点。…

后端返回一个图片链接,前端如何实现下载功能?

纯原创文章&#xff0c;转载请说明来源。 一、背景 要实现一个下载功能&#xff0c;后端直接返回了一个图片的地址https://xxxxx/pic.jpg。如果我们直接通过window.open(url, _blank) 的方式去下载这个图片&#xff0c;会发现 Chrome 浏览器会对这个图片进行预览&#xff0c;…

魅族手机怎么录屏?详细步骤助你轻松上手

“有人知道魅族手机怎么录屏吗&#xff0c;最近我在准备一些教学视频&#xff0c;急需用到手机的录屏功能来记录操作过程&#xff0c;但遗憾的是&#xff0c;我翻遍了设置也没能找到录屏的开关。所以&#xff0c;我在这里想问问大家&#xff0c;魅族手机是如何启动录屏功能的&a…

【PyTorch】图像多分类项目部署

【PyTorch】图像多分类项目 【PyTorch】图像多分类项目部署 如果需要在独立于训练脚本的新脚本中部署模型&#xff0c;这种情况模型和权重在内存中不存在&#xff0c;因此需要构造一个模型类的对象&#xff0c;然后将存储的权重加载到模型中。 加载模型参数&#xff0c;验证模型…

图解 HDFS 架构 |读写过程

HDFS HDFS 全称 Hadoop Distributed File System&#xff0c;是一个分布式文件系统。HDFS&#xff08;Hadoop Distributed File System&#xff09;是 Apache Hadoop 生态系统的一部分&#xff0c;它是一个分布式文件系统&#xff0c;用于存储和处理大规模数据集。HDFS 专门设…

源代码防泄密如何做?企业如何有效选择源代码防泄密产品?

源代码防泄密怎么选&#xff1f;如何高效做源代码防泄密工作&#xff1f; 源代码开发环境复杂&#xff0c;涉及的开发软件和文件类型众多且变化多端&#xff0c;那么究竟有哪些源代码防泄密软件能够适应各种开发软件而不影响原有的工作效率呢&#xff1f; 对于研发人员来说&a…

探索 Framer Motion 高级动画技巧:提升前端设计水平

在现代的网页和应用设计中&#xff0c;动画不仅仅是视觉的点缀&#xff0c;更是用户体验的重要组成部分。它能够使界面更具吸引力&#xff0c;提升交互的流畅性&#xff0c;甚至在不经意间传达品牌的个性和态度。然而&#xff0c;要创造出令人惊叹的动效并不容易——直到有了 F…

经验——OLED的使用

型号&#xff1a;HS96L01W 4S03 分辨率&#xff1a;120*64 通讯方式&#xff1a;4线SPI 模式00 MCU&#xff1a;MSPM0G3507&#xff08;只影响SPI的配置&#xff09; 原本照着型号搜到了嘉立创的使用文档&#xff0c;但是实际上并不能正常使用&#xff0c;后来寻到了一篇博客…

MFC与QT中禁用Esc、Alt+F4、关闭图标

在业务中&#xff0c;我们需要按指定的方式才能关闭当前对话框。如下图需输入密码点击确认后&#xff0c;界面才能关闭。 方法1&#xff1a;通过禁用界面的按钮以及键盘上对应关闭对话框的按键。 1.灰度化关闭按钮 在对话框初始化部分添加将关闭按钮禁用 //MFC CMenu *pSysMe…

主要的国产信创数据库有哪些

数据库生态分类 当前数据库生态可以大致分类三类: 一、传统商业数据库&#xff0c;以 Oracle 为代表&#xff0c;其在 40 余年时间里所创造的数据库帝国已拥有了极其完善的生态; 二、开源数据库&#xff0c;以 MYSQL、PostgreSQL为代表&#xff0c;遍布全球的社区组织形成了强…

大文件分片上传(前端TS实现)

大文件分片上传 内容 一般情况下&#xff0c;前端上传文件就是new FormData,然后把文件 append 进去&#xff0c;然后post发送给后端就完事了&#xff0c;但是文件越大&#xff0c;上传的文件也就越长&#xff0c;如果在上传过程中&#xff0c;突然网络故障&#xff0c;又或者…

AHK是让任何软件都支持 Shift + 鼠标滚轮 实现界面水平滚动

目录 基本介绍 详细特点 图解安装 下载失败&#xff1f;缓慢&#xff1f; 创建并运行脚本代码&#x1f603; 新建空 xxx.ahk文件 vscode/记事本等编辑工具打开 复制并粘贴简易脚本 运行 其他问题 问题一&#xff1a;弹出无法执行此脚本 关闭脚本 基本介绍 AutoHot…

【MetaGPT系列】【MetaGPT完全实践宝典——如何定义单一行为多行为Agent】

目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent&#xff08;ProductManager&#xff09;2-3、拥有单一行为的Agent&#xff08;SimpleCoder&#xff09;2-3-1、定义写代码行为2-3-2、角色定义2-3…

B站音视频分开 大小问题

音频是33331 kb&#xff0c;视频是374661 kb 合并之后却是2561363 kb 这可能是B站音频和视频分开的原因吧

Zabbix监控案例

文章目录 一、监控linux TCP连接状态TCP端口的十一种连接状态自定义监控项监控示例二、监控模板监控tcp连接监控nginx 一、监控linux TCP连接状态 TCP&#xff0c;全称Transfer Control Protocol&#xff0c;中文名为传输控制协议&#xff0c;它工作在OSI的传输层&#xff0c;…

3.Fabric系统架构、网络拓扑图、交易流程

Hyperledger Fabric系统架构 Fabric网络拓扑图 Fabric交易流程 多通道

【数字范围按位与】python刷题记录

run到位运算。 顿悟&#xff1a; 只看第一个二进制位&#xff0c;只存在0,1两种情况&#xff0c;所以如果left<right&#xff0c;区间中必然存在left1,那么最低位&一下一定等于0了&#xff0c;然后不停的右移&#xff0c;一直移到两个相等为止&#xff0c;就这么简单 …

Qt自定义下拉列表-可为选项设置标题、可禁用选项

在Qt中,ComboBox&#xff08;组合框&#xff09;是一种常用的用户界面控件,它提供了一个下拉列表,允许用户从预定义的选项中选择一个。在项目开发中&#xff0c;如果简单的QComboBox无法满足需求&#xff0c;可以通过自定义QComboBox来实现更复杂的功能。本文介绍一个自定义的下…

二级医院LIS系统源码,医学检验系统,支持DB2,Oracle,MS SQLServer等主流数据库

系统概述&#xff1a; LIS系统即实验室信息管理系统。LIS系统能实现临床检验信息化&#xff0c;检验科信息管理自动化。其主要功能是将检验科的实验仪器传出的检验数据经数据分析后&#xff0c;自动生成打印报告&#xff0c;通过网络存储在数据库中&#xff0c;使医生能够通过医…