【Linux】信号知识三把斧——信号的产生、保存和处理

news2024/11/26 11:41:10

目录​​​​​​​

1、关于信号的前置知识

1.1.什么是信号?

1.2.为什么要学习信号?

1.3.如何学习信号?

1.4.一些常见的信号

1.5.信号的处理方式

1.6.为什么每一个进程都可以系统调用?

2.信号的产生

2.1.kill命令产生信号

2.2.键盘产生信号

CTRL+c和CTRL+\的区别

2.3.调用系统函数向进程发信号

2.4.软件条件产生信号

2.5.异常产生信号(硬件异常)

2.6.信号产生的小总结

3.信号的保存

3.1三张表基础

阻塞vs忽略:

3.2三张表匹配的操作和系统调用

3.3.Core和Term

如何打开Linux的core功能呢?

为什么要用核心转储功能呢?

4.信号的处理

4.1.信号什么时候被处理的?

4.2.信号是如何被处理的?

4.3.volatile

1、关于信号的前置知识

1.1.什么是信号?

Linux系统提供的让用户(进程)给其他进程发送异步信息的一种方式。(注意信号和信号量这两者没有任何关系!)

举个例子:

用户输入命令,在Shell下启动一个前台进程。用户按下 Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程前台进程因为收到信号,进而引起进程退出~

进程就是你,操作系统就是快递员,信号就是快递

1.2.为什么要学习信号?

我们平时在处理进程的时候,对于停止、删除进程等操作,系统要求进程要有随时响应外部信号的能力,随后做出反应

1.3.如何学习信号?

根据进程对于整体信号的操作过程进行学习。

  1. 信号的产生(kill命令和键盘产生信号)
  2. 信号的保存
  3. 信号的处理

1.4.一些常见的信号

用kill -l命令可以察看系统定义的信号列表

数组和名字都可以标识一个信号,名字其实就是宏,注意没有信号0,没有信号32和33

1.5.信号的处理方式

  • 信号自己的默认动作
  • 自定义处理信号,捕捉信号
  • 忽略信号,忽略也代表处理过信号了

所以我们自己是可以更改对信号的处理方式。

1.6.为什么每一个进程都可以系统调用?

写时拷贝的时候拷贝的全部都是用户空间,不会拷贝内核空间

每一个进程都有自己的地址空间,多个进程就会有多个地址空间,但是内核空间只有一份。所以任何一个进程都可以系统调用

2.信号的产生

2.1.kill命令产生信号

当我们输入kill命令去给进程发送信号的时候,本质是OS进行操作的。

2.2.键盘产生信号

键盘如何产生信号呢?

常见的有CTRL+c,代表中断这个程序;CTRL+ \发送SIGQUIT信号给当前进程,导致该进程退出并生成core转储文件

CTRL+c和CTRL+\的区别

CTRL+\与Ctrl+C不同,后者只是发送SIGINT信号给当前正在运行的进程,导致进程被终止。Ctrl+\会生成core文件,这个文件包含了进程在退出时的内存映像,可以用于调试。如果进程成功生成core文件,那么可以使用调试工具来分析这个文件,以了解进程崩溃时的状态,这对于排查问题非常有帮助‌。

2.3.调用系统函数向进程发信号

kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。

  • kill函数对任意进程发送任意的信号
  • raise函数对自己发送任意信号

#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1

abort函数使当前进程接收到信号而异常终止

#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。

2.4.软件条件产生信号

alarm函数 和SIGALRM信号就是由软件条件产生信号的代表

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动
作是终止当前进程。

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。如果参数seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

2.5.异常产生信号(硬件异常)

  • 代码除零产生异常
  • 野指针异常

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。

例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。

再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

注意寄存器只有一个,但是寄存器的数据可以有很多,我们把寄存器中的数据叫做:上下文数据!!!

2.6.信号产生的小总结

当信号产生的时候,如果进程在处理更加重要的事情,我们就暂时不能处理到来的信号,我们必须暂时要将到来的信号进行临时保存。

那么问题来了,我们将这些信号保存在哪里呢?——进程的PCB中

所以只有OS才有资格写入信号,如果用户也想写入信号,就必须使用OS提供的系统调用。因此,无论信号产生的方式有多少种,最终都是OS亲自动手将信号写入进程的!!!

3.信号的保存

3.1三张表基础

理论上来说我们用3张表就可以保存信号

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

阻塞vs忽略:

忽略是一种信号递达的方式。阻塞仅仅是不让指定信号进程递达

  • pending表比特位的位置,表示信号编号,比特位的内容,表示是否收到指定信号
  • block表的比特位的位置,表示信号编号,比特位的内容,表示是否阻塞该信号

下面这三张表需要我们横着读,最后一个handler表示对信号的处理方法

这三张表分别表示信号是否阻塞,信号是否接受到,信号的处理动作

所以我们看这张表的时候,不是竖着看,而是横着看每一个信号

3.2三张表匹配的操作和系统调用

block表对应的是sigprocmask函数

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

pending表对应的是sigpending函数

#include <signal.h>
sigpending
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

handler函数对应的是signal函数。

可以对指定的信号进行用户指定的信号处理。更改信号的处理方式。

下面是利用这几个函数进行编码,小试牛刀。 

3.3.Core和Term

大多数信号的默认响应行为都是Core或者Term;

这两种信号都表示终止进程

区别:

  • Term就是普通的终止进程,之后没有其他动作。
  • Core不仅会终止进程,还会生成一个核心转储文件

为什么默认关闭核心转储功能?防止未知的core dump 一直在进行,导致服务器磁盘被打满,所以默认core是关闭的。

如何打开Linux的core功能呢?

使用ulimit -a查看当前资源限制的设定 ;

其中,第一行显示core文件的大小为0,即表示核心转储是被关闭的

通过ulimit -c size 命令来设置Core文件的大小(同时也是打开了核心转储

为什么要用核心转储功能呢?

想通过core定位到进程为什么退出,以及执行到哪行代码退出的

核心转储功能是什么?

将进程在内存中的核心数据(与调试有关)转储到磁盘中形成。

有什么用呢?

协助我们进行调试!

4.信号的处理

4.1.信号什么时候被处理的?

合适的时候,什么是合适的时候呢?进程从内核态(操作系统的状态,权限级别高),切换到用户态(你自己的状态)的时候,信号会被检测并处理

在信号处理的过程(捕捉)中,一共会有4次的状态切换(内核和用户态)

4.2.信号是如何被处理的?

我们使用系统调用或者访问系统数据,其实还是在进程的地址空间内进行切换的

进程无论如何被切换,总能找到OS,我们访问OS,本质就是通过我们的进程的地址空间进行访问

4.3.volatile

volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作

 

编译器正常处理是将flag的值从内存读取到CPU中进行处理

当前编译器做了一个优化,因为系统认为flag的值定义之后就没有改变,因此直接将内存里flag的值直接放在了CPU 的寄存器中,因此后面代码改变flag值的时候,是在内存当中改变的,CPU中的值不会改变,而程序读取数据是从CPU读取的,因此就会造成偏差,这时候就需要我们的volatile去修饰这个变量,默认从内存中读取!​​​​​​​

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

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

相关文章

[Python学习日记-40] 函数进阶之装饰器

[Python学习日记-40] 函数进阶之装饰器 简介 引子 什么是装饰器 装饰器终结版 装饰器的层层叠加 简介 在前面铺垫了这么多终于该讲到重点了&#xff0c;前面说的匿名函数、高阶函数、闭包等等都是为了这篇文章所讲的装饰器而使用的&#xff0c;本篇文章将会一一个故事通俗…

个人网站,怎么操作才能提升个人网站的流量

运营个人网站以提升流量是一个综合性的过程&#xff0c;涉及内容优化、技术调整、用户体验提升以及外部推广等多个方面。以下是一些专业建议&#xff0c;旨在帮助个人网站运营者有效提升网站流量&#xff1a; 1.精准关键词研究与优化 -关键词研究&#xff1a;利用工具如谷歌…

关于 Python 3.13 你所需要知道的几点

什么是全局解释器锁 (GIL)&#xff1f; 自20世纪80年代末&#xff0c;Guido Van Rossum在荷兰阿姆斯特丹东部的一个科技园区开始开发Python编程语言&#xff0c;它最初被设计为一种单线程的解释型语言。这到底是什么意思呢&#xff1f; 你可能会听说&#xff0c;编程语言分为解…

C++-容器适配器- stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…

IDEA 2024.3 预览:把开发者感动到哭了

幸运的人&#xff0c; 一生都被童年治愈&#xff1b; 不幸的人&#xff0c; 一生都在治愈童年 只有勇敢的人 和有钱的人才能先享受世界 缘分就是我还不知道 会见到你就误打误撞般 遇见了你 最近 IDEA 又发布了最新的 2024.3 的预览版本 EAP&#xff0c;把开发者的心激动的…

躺平成长-第四周的开发日记

回顾自己的小程序&#xff0c;现在自己有饮食/跑步/养生操/学习/学历提升/等多样化的kp值计算公式页面&#xff0c;自己tarbar导航页面有两个内容&#xff0c;kp值计算&#xff0c;躺平显示&#xff0c;单纯这些功能自己使用下来&#xff0c;会感到一种疲惫&#xff01;&#x…

详解Java中的堆内存

详解Java中的堆内存 堆是JVM运行数据区中的一块内存空间&#xff0c;它是线程共享的一块区域&#xff08;注意了&#xff01;&#xff01;&#xff01;&#xff09;&#xff0c;主要用来保存数组和对象实例等&#xff08;其实对象有时候是不在堆中进行分配的&#xff0c;想要了…

霓虹灯数字时钟(可复制源代码)

文章目录 一、效果演示二、CodeHTMLCSSJavaScript 三、实现思路拆分CSS 部分JavaScript 部分 四、源代码 一、效果演示 文末可一键复制完整代码 二、Code HTML <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><…

我对软件工程的理解

1 引言 从事软件行业这么年&#xff0c;写了10年代码&#xff0c;又从事了多年的项目产品方面的工作&#xff0c;一些每天用到的软件工程的方法&#xff0c;虽然天天都在用但一些概念总感觉似是而非&#xff0c;正好借假期的时间&#xff0c;好好整理下&#xff0c;以供自己或…

AI配音(声音克隆)

Fish Audio: Free Generative AI Text To Speech & Voice Cloning 【【AI配音】终于找到免费 & 小白友好的声音克隆软件了&#xff01;真人相似度98%!】https://www.bilibili.com/video/BV1MwbFeCE2X?vd_source3cc3c07b09206097d0d8b0aefdf07958 我终于找到总这3款免…

Java中的封装、继承、多态

目录 封装 概念 包 继承 多态 向上转型 一、直接赋值 二、方法传参 三、返回值 向上转型注意事项 向下转型 格式 重写 重写和重载的区别 动态绑定 静态绑定和动态绑定 封装 概念 简单来说就是套壳屏蔽细节。 举例&#xff1a; 想要访问它们时需要一些“接口”…

Codeforces Rund 977 div2 个人题解(A~E1)

Codeforces Rund 977 div2 个人题解(A,B,C1,C2,E1) Dashboard - Codeforces Round 977 (Div. 2, based on COMPFEST 16 - Final Round) - Codeforces 火车头 #define _CRT_SECURE_NO_WARNINGS 1#include <algorithm> #include <array> #include <bitset> …

Java之二叉树的基本操作实现

1. 模拟实现二叉树前&#xff0c;我们要先表示树&#xff0c;首先定义一个内部类&#xff0c;当作二叉树节点 static class TreeNOde{char val;//存放二叉树的值TreeNOde left;//指向左子树的引用TreeNOde right;//指向右子树的引用//构造方法&#xff0c;用于实例化树的节点p…

信息学奥赛复赛复习13-CSP-J2021-02插入排序-排序稳定性、插入排序、sort排序、结构体、计数排序

PDF文档回复:20241006 1P7910 [CSP-J 2021] 插入排序 [题目描述] 插入排序是一种非常常见且简单的排序算法。小 Z 是一名大一的新生&#xff0c;今天 H 老师刚刚在上课的时候讲了插入排序算法。 假设比较两个元素的时间为 O(1)&#xff0c;则插入排序可以以 O(n^2) 的时间复…

第五节——转移表(让你不再害怕指针)

文章目录 制作简易计算器什么是转移表&#xff1f;switch函数实现函数指针数组实现 制作简易计算器 要求&#xff1a;制作一个简易计算器&#xff0c;可以进行* / - 等功能运算。 什么是转移表&#xff1f; 指的就是通过函数指针数组的方式通过数组去调用里面的函数&#x…

LeetCode讲解篇之239. 滑动窗口最大值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们维护一个长度为k的窗口&#xff0c;然后窗口从数组最左边一直移动到最右边&#xff0c;记录过程中窗口中的最大值&#xff0c;就是答案 我们每次查询长度为k的窗口最大值是什么时间复杂度是O(k)的&#xff0…

软件验证与确认实验二-单元测试

目录 1. 实验目的及要求.................................................................................................... 3 2. 实验软硬件环境.................................................................................................... 3 …

idea插件市场安装没反应

https://plugins.jetbrains.com/idea重启后还是不行那就

163页PPT罗兰贝格品牌战略升级:华为案例启示与电器集团转型之路

罗兰贝格作为一家全球顶级的战略管理咨询公司&#xff0c;其品牌战略升级理念在多个行业中得到了广泛应用。以下将以华为案例为启示&#xff0c;探讨电器集团的转型之路&#xff0c;并融入罗兰贝格品牌战略升级的思想。 一、华为案例的启示 华为与罗兰贝格联合撰写的《数据存…

基于java+springboot的酒店预定网站、酒店客房管理系统

该系统是基于Java的酒店客房预订系统设计与实现。是给师弟开发的毕业设计。现将源代码开放出来&#xff0c;感兴趣的同学可以下载。 演示地址 前台地址&#xff1a; http://hotel.gitapp.cn 后台地址&#xff1a; http://hotel.gitapp.cn/admin 后台管理帐号&#xff1a; 用…