Linux进程信号处理:深入理解与应用(2​​)

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:it's 6pm but I miss u already.—bbbluelee

                                                                0:01━━━━━━️💟──────── 3:18
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

前言

进程信号的产生

1、通过终端按键产生信号。

OS怎么知道终端按键(即:键盘)有数据了呢?

常见的通过按键产生的信号及其作用

Core Dump

2、调用系统函数向进程发信号。

kill

其他

3、由软件条件产生信号。

4、硬件异常产生信号。


前言

        本文书接上回Linux进程信号处理:深入理解与应用(1),主要是对于信号的产生中四种主要的产生方式进行详细的解析。哪四种呢?1、通过终端按键产生信号。2、调用系统函数向进程发信号。3、由软件条件产生信号。4、硬件异常产生信号。

进程信号的产生

1、通过终端按键产生信号。

OS怎么知道终端按键(即:键盘)有数据了呢?

        这个主要是通过中断的方式让OS知道的!我们都知道CPU是装在主板上的,而CUP有很多针脚,这些针脚通过对应的线路与主板相连接,而实际上我们的外设也会通过线路与主板相连接。(当然,外设那么多,主要是通过一个主板上的一个叫8259(中断控制器)的来级联)当我们通过终端按键按下按钮的时候,透过导线和主板,CPU会将对应传递的电信号透过一定的标记手段将对应的内容储存到寄存器中。这时OS就知道哪个外设已经就绪了。这时就相当于把硬件就绪转换成了寄存器中的某种数据(也就是中断号)。此时,这些数据就可以被程序读取了。也就是说硬件行为被转换成了软件行为。再接着,OS通过相应的驱动就可以读取对应的数据到内存中处理。最后,根据开机就会启动的一张中断向量表,按特定的读取方法读取数据。大致图示:

常见的通过按键产生的信号及其作用
  • SIGINT (2):通常由Ctrl + C产生,这个信号会中断当前进程,常常用来终止正在运行的程序。
  • SIGTSTP (20):由Ctrl + Z产生,此信号会使进程暂停并转入后台。之后可以通过fg命令将进程重新带回到前台继续运行。
  • SIGQUIT (3):通过Ctrl + \产生,该信号不仅会终止前台进程组中的所有进程,还会生成core文件,用于调试。

Core Dump

        在signal 7 手册中有这样一张表,其中Action表示该信号的执行方式,其中Term是terminal的意思表示为终止,而Core也是终止的意思,完整叫Core Dump,叫做核心转储,他会在进程崩溃时会形成以进程pid命名的code.pid文件或者pid.code(不同系统不一样),通常在该进程的同一目录下,将进程崩溃时的核心上下文数据转储到该文件中。

​        如下为对应执行方式详细解释:

        在Linux操作系统中,"core"文件是当进程异常终止时由操作系统生成的。

作用和重要性

  • 调试信息源:它包含了进程崩溃时的内存映像和部分相关的调试信息,这对于开发人员来说是非常宝贵的资料,有助于快速定位问题所在。
  • 故障分析:通过分析core文件,可以了解导致程序崩溃的具体原因,比如指针错误、内存泄漏等。
  • 重现问题:core文件可以帮助开发人员在没有源代码的情况下重现问题,从而进行更有效的问题修复。

如何生成core文件

  • 信号触发:有多种信号可能导致core文件的生成,例如SIGSEGV(段错误)、SIGFPE(浮点异常)等。当这些信号被发送到进程且未被捕获或忽略时,系统会创建core文件。
  • ulimit命令:可以使用ulimit -c unlimited命令来确保在进程崩溃时生成core文件。
  • /proc/sys配置:编辑/proc/sys/kernel/core_pattern文件可以设置core文件的命名和存储路径。

如何使用core文件

  • gdb工具:使用gdb加载可执行文件和core文件,可以查看崩溃时的调用栈、变量值等信息,帮助定位问题。
  • 其他工具:除了gdb,还有其他一些工具如addr2line、libbfd等也可以用于分析core文件。

存储位置和管理

  • 存储位置:默认情况下,core文件通常存储在与崩溃进程相同的目录下,但可以通过修改core_pattern来改变存储位置。
  • 空间问题:由于core文件可能非常大,它们可能会占用大量磁盘空间。因此,对于磁盘空间有限的系统,需要谨慎管理core文件的生成。
  • 大小限制:可以通过ulimit -c命令设置core文件的大小限制,防止过大的core文件占用过多磁盘空间。
  • 禁止生成:如果不需要core文件,可以将ulimit -c设置为0或者通过echo "/dev/null" > /proc/sys/kernel/core_pattern来禁止core文件的生成。

        如下例子:

        通过ulimit -a 查看对应的code file size,如果为0则不会生成code文件,因此我们通过ulimit -a 需要的大小,来设置使他可以生成code文件:

        发生Floating point exception错误,生成对应pid的code文件:

        如下是通过gdb与code文件的使用:

2、调用系统函数向进程发信号。

kill

        在Linux系统中,kill函数是用于向进程发送信号的核心系统调用。它允许一个进程影响另一个进程的执行流程,包括终止、暂停或继续执行等操作。以下是kill函数的详细解释:

函数原型

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

参数

  • pid:要接收信号的进程的进程ID(PID)。可以是一个正数(表示具体进程),0(表示与当前进程相同的进程组),或者是1(表示所有进程,除了init进程)。
  • sig:要发送的信号的编号。常见的信号包括SIGTERM(终止信号)、SIGKILL(强制终止信号)、SIGSTOP(停止信号)和SIGCONT(继续运行信号)等。

返回值

        如果成功,kill函数返回0;如果失败,它将返回-1,并设置errno以指示错误。

错误

  • ESRCH:找不到与指定PID相匹配的进程。
  • EPERM:调用者没有足够的权限发送信号给目标进程。
  • EINVAL:提供了无效的信号编号。

        如下例子:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>int main() {pid_t pid = fork(); // 创建子进程if (pid == 0) {// 子进程中while (1) {printf("Child process is running...
");sleep(1);}} else if (pid > 0) {// 父进程中,发送SIGTERM信号给子进程kill(pid, SIGTERM);printf("Signal sent to child process.
");}return 0;
}

        在这个例子中,父进程通过kill函数向其子进程发送了一个SIGTERM信号,导致子进程被终止。

其他

        raise、abort在Linux进程信号处理:深入理解与应用(1)有详细介绍。

3、由软件条件产生信号。

        我们在介绍管道的时候,我们打开一个管道,其中一端在写一端在读,当我们将读端关闭了后我们再向管道去写,会发现出现了SIGPIPE信号,然后会发现这个进程就结束了。这是因为通过操作系统对于管道读描述符是否被打开了等等,检测了内核数据结构的情况某些字段是否有效来管理进程,在发现读端没了后,进程就被杀死了。很明显,这是通过软件来产生的信号。因为操作系统是软硬件的管理者,信号当然可以由软件产生,也可以由硬件产生!当然,并不是所有的信号都是杀死进程,也有诸如:SIGTSTP暂停信号。以下是一些常见的软件条件产生的信号及其产生方式:

  1. SIGTERM (15):这是默认的信号,当kill命令不带任何参数时会发送这个信号。它通常用于请求进程正常终止,允许进程执行清理操作。
  2. SIGKILL (9):这是一个强制终止进程的信号,不允许进程捕获或忽略。通常,kill -9命令用于发送此信号。
  3. SIGINT (2):通常由Ctrl + C产生,但也可以通过kill命令发送。它通常用于中断前台运行的进程。
  4. SIGQUIT (3):可以通过kill -3命令发送。与SIGKILL不同,SIGQUIT允许进程在终止前执行清理操作,并且会产生core dump。
  5. SIGHUP (1):通常用于通知进程重新加载配置文件。例如,当修改了Web服务器的配置文件后,可以发送SIGHUP信号使其重新加载配置。
  6. SIGUSR1和SIGUSR2 (10, 12):这两个信号可以被进程自定义使用,通常用于用户定义的信号处理。
  7. SIGALRM (14):当定时器到期时,内核会向进程发送SIGALRM信号。
  8. SIGCHLD (20):当子进程停止或终止时,会向父进程发送SIGCHLD信号。
  9. SIGIO (29):当I/O操作完成时,内核会向进程发送SIGIO信号。
  10. SIGWINCH (28):当窗口大小发生变化时,会向进程发送SIGWINCH信号。

        下面介绍一下其中比较重要的alarm函数和SIGALRM信号:

函数原型

#include <unistd.h>unsigned int alarm(unsigned int seconds);

参数

  • alarm函数接受一个无符号整数seconds作为参数,表示定时器的时间长度。

返回值

  • 如果之前已经设置了闹钟,则alarm函数返回上一个闹钟剩余的秒数;如果没有设置过闹钟,则返回0。在错误情况下,alarm函数可能返回-1,但这通常意味着有错误发生。

        它接受一个参数seconds,表示从当前时刻开始计时,经过这段时间后,内核将向当前进程发送SIGALRM信号。如果进程没有特别设置对SIGALRM信号的处理函数,那么默认的动作是终止该进程。此外,alarm函数还有一些其他重要的特性:

覆盖性

  • 如果在seconds秒内再次调用了alarm函数并设置了新的闹钟,原来的闹钟将被覆盖。

常见用途

  1. 定时任务:可以使用alarm函数来设置一个定时器,当达到指定时间后执行某个任务或操作。
  2. 超时检测:可以结合alarm函数和信号处理机制,实现对某个操作的超时检测。
  3. 资源释放:在某些情况下,可能需要在一定时间后自动释放某些资源,这时可以使用alarm函数来实现。

        下面看一个例子:假设我们有一个程序需要在一定时间内完成某个任务,如果超过这个时间还没有完成,就发送一个警告消息并终止程序。我们可以使用alarm函数来实现这个功能。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void timeout_handler(int signum) {printf("任务超时!\n");exit(1);
}int main() {// 设置超时时间为5秒alarm(5);// 注册超时处理函数signal(SIGALRM, timeout_handler);// 模拟一个耗时的任务sleep(6);return 0;
}

        在这个例子中,我们首先使用alarm函数设置了一个5秒的定时器。然后,我们注册了一个超时处理函数timeout_handler,当定时器到期时,这个函数会被调用。最后,我们模拟了一个耗时6秒的任务,超过了我们设置的5秒超时时间,所以程序会输出"任务超时!"并终止。

注意事项

  1. 与其他信号处理机制的交互影响alarm函数设置的定时器是基于系统时钟的,而系统时钟可能会受到各种因素的影响,如系统负载、硬件性能等。因此,在使用alarm函数时,需要注意其精度可能不如实时操作系统中的定时器。
  2. 在不同Linux发行版中的实现差异:虽然alarm函数在大多数Linux发行版中都有相同的实现,但在某些特定的发行版中可能会有一些差异。因此,在使用alarm函数时,最好查阅相关文档以确保其正确性和兼容性。

4、硬件异常产生信号。

        硬件是怎么产生信号的呢?当我们在运行时,代码或者某些问题导致硬件出现了异常,CPU可以直接向操作系统发出信号,OS会将发出的信号进行解释,会解释类似于kill(pid_t pid, int sig);,然后向目标进程发送特定的信号。此时就由硬件问题转化成了信号问题,可以让进程直接终止。在Linux系统中,当硬件发生异常时,内核会向进程发送信号。以下是一些常见的硬件异常信号及其解释:

  1. SIGBUS(7):总线错误。通常由于对齐问题或访问不存在的内存地址导致。
  2. SIGSEGV(11):段错误。通常是由于访问无效内存地址导致,如空指针解引用、访问已释放的内存等。
  3. SIGILL(4):非法指令。当程序尝试执行非法或不支持的指令时产生。
  4. SIGTRAP(5):陷阱指令。当程序执行陷阱指令(如断点)时产生。
  5. SIGFPE(8):浮点异常。当程序执行浮点运算时发生错误,如除以零、溢出等。
  6. SIGSTKFLT(16):协处理器栈错误。当协处理器栈溢出或发生其他错误时产生。
  7. SIGSYS(38):系统调用错误。当程序执行系统调用时发生错误,如参数错误、系统调用号错误等。
  8. SIGEMT(72):EMT指令错误。当程序执行EMT(Intel扩展内存类型)指令时发生错误。
  9. SIGPWR(39):电源故障。当系统电源发生故障时产生。
  10. SIGXCPU(24):CPU故障。当CPU发生错误时产生。
  11. SIGXFSZ(25):文件尺寸错误。当程序尝试读取文件时,文件尺寸发生变化导致错误。
  12. SIGVTALRM(26):虚拟定时器到期。当程序设置的虚拟定时器到期时产生。
  13. SIGPROF(27):性能计数器超时。当程序的性能计数器达到设定值时产生。
  14. SIGWINCH(28):窗口大小改变。当终端窗口大小发生改变时产生。
  15. SIGURG(16):紧急数据可用。当网络连接上出现紧急数据时产生。
  16. SIGIO(29):I/O请求完成。当异步I/O请求完成时产生。
  17. SIGPENDING(30):挂起信号。当有未处理的信号被捕获时产生。
  18. SIGPOLL(31):流事件。当流设备上有事件发生时产生。
  19. SIGRTMIN(32):实时信号最小值。用于自定义实时信号的起始值。
  20. SIGRTMAX(33):实时信号最大值。用于自定义实时信号的结束值。

        下面我们以8号信号为例子看一段代码的运行结果:

        我们通过让变量a除0导致浮点异常,再使用signal更改了8号信号的处理方法。通过以上的效果,我们发现程序现了死循环的情况,然而我们在程序中并为有死循环的操作,这是为什么呢?因为当一个进程出现了异常后,我们的操作系统默认是终止这个进程的,把进程杀死,默认就是处理问题的方式之一,为的是让操作系统恢复健康。而如今我们改变了处理方法且并没有退出进程的方法,异常就不会退出了,他会一直存在,CPU只要调度这个进程就会出现异常,然而这个进程也没杀死,进程虽然不会继续往后执行,但是异常还是存在,而CPU调度完其他进程后,再次调度这个进程又会出现这样的情况。本质就是异常没有被终止,进程一直被调度!

 

        PS:Linux浩如烟海,信号还没完!下一篇继续!

 


                    感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

 

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

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

相关文章

ElementUI Form:Form表单

ElementUI安装与使用指南 Form表单 点击下载learnelementuispringboot项目源码 效果图 el-form.vue&#xff08;Form表单&#xff09;页面效果图 项目里 el-form.vue代码 <script> export default {name: el_form,data() {var checkAge (rule, value, callback…

计算机设计大赛 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

中等题 ------ 链表

文章目录 1. 删除链表中的倒数第N个节点&#xff08;1&#xff09;栈&#xff08;2&#xff09;双指针&#xff08;快慢指针&#xff09; 2. 交换链表中的节点3. 两数相加4. 合并两个链表5. 旋转链表6. 奇偶链表7. 两两交换8. k 个一组翻转链表9. 分割链表10. 分隔链表11. 重排…

19.HarmonyOS App(JAVA)依赖布局DependentLayout使用方法

layout/ability_main.xml 显示位置不对&#xff1a;检查布局文件ohos:lef_of "id:tuzi",比如显示在兔子的左侧&#xff0c;这里就会显示不对。 需要id前没有$符号。改为&#xff1a; ohos:lef_of "$id:tuzi" <?xml version"1.0" encodi…

第六讲:文件操作

第六讲:文件操作 文件夹创建文件夹移动文件夹复制文件夹删除文件夹文件操作文件读取文件写入文件文件夹 创建文件夹 定义创建文件夹函数:chmk_path()定义一个函数 chmk_path(),这个函数的功能是创建文件夹。 首先需要导入操作系统接口模块——os 模块,这个模块中包含某些函…

前端小案例——滚动文本区域(HTML+CSS, 附源码)

一、前言 实现功能: 这个案例实现了一个具有滚动功能的文本区域&#xff0c;用于显示长文本内容&#xff0c;并且可以通过滚动条来查看完整的文本内容。 实现逻辑&#xff1a; 内容布局&#xff1a;在<body>中&#xff0c;使用<div>容器创建了一个类名为listen_t…

5.0 HDFS 集群服务建立教程

HDFS 集群是建立在 Hadoop 集群之上的&#xff0c;由于 HDFS 是 Hadoop 最主要的守护进程&#xff0c;所以 HDFS 集群的配置过程是 Hadoop 集群配置过程的代表。 使用 Docker 可以更加方便地、高效地构建出一个集群环境。 每台计算机中的配置 Hadoop 如何配置集群、不同的计…

【实战系列----消息队列 数据缓存】rabbitmq 消息队列 搭建和应用

线上运行图&#xff0c;更新不算最新版&#xff0c;但可以使用修改线程等补丁功能&#xff0c;建议使用新版本。 远程服务器配置图: 这个可以更具体情况&#xff0c;因为是缓存队列理所当然 内存越大越好&#xff0c;至于核心4核以上足够使用。4核心一样跑 这里主要是需要配置服…

2024美赛数学建模E题思路+代码

文章目录 1 赛题思路2 美赛比赛日期和时间3 赛题类型4 美赛常见数模问题5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 美赛比赛日期和时间 比赛开始时间&#xff1a;北京时间2024年2月2日&#xff08;周五&#xff…

断路精灵:探秘Sentinel熔断策略的神奇效果

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 断路精灵&#xff1a;探秘Sentinel熔断策略的神奇效果 前言熔断策略基础&#xff1a;数字断路精灵的初见熔断策略的基本原理&#xff1a;简单示例演示熔断策略的基本用法&#xff1a; 慢调用比例熔断策…

计算机网络-差错控制(奇偶校验码 CRC循环冗余码)

文章目录 差错从何而来从传感器层面提高信道比来减少线路本身的随机噪声的一个例子热噪声和冲击噪声 数据链路层的差错控制检错编码-奇偶校验码检错编码-CRC循环冗余码例子注意 差错从何而来 噪声通常指的是任何未预期的、随机的信号干扰&#xff0c;这些干扰可能源自多种物理…

linux ln命令-linux软链接、硬链接-linux软、硬链接的区别(一):硬链接

0、序 1、硬链接 1.1、创建硬链接的注意事项 (1)、硬链接不能指向目录&#xff08;不能对目录文件做硬链接&#xff09;。 (2)、硬链接只能在同一个文件系统中创建&#xff0c;不能在不同的文件系统之间做硬链接。就是说&#xff0c;链接文件和被链接文件必须位于同一个文件…

java设计模式:观察者模式

在平常的开发工作中&#xff0c;经常会使用到设计模式。合理的使用设计模式&#xff0c;可以提高开发效率、提高代码质量、提高代码的可拓展性和维护性。今天来聊聊观察者模式。 观察者模式是一种行为型设计模式&#xff0c;用于对象之间一对多的依赖关系&#xff0c;当被观察对…

vue3+threejs+koa可视化项目——模型文件上传(第四步)

文章目录 ⭐前言&#x1f496;往期node系列文章&#x1f496;threejs系列相关文章&#x1f496;vue3threejs系列 ⭐koa后端文件上传(koa-body)&#x1f496;自动创建目录&#x1f496;自定义目录上传&#x1f496;apifox自测上传接口 ⭐vue3前端上传模型文件&#x1f496; axio…

LeetCode --116

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

代码编辑器1.9.0

多线程&#xff01;&#xff01;&#xff01; #include <winsock2.h> #include <windows.h> #include <iostream> #include <stdlib.h> #include <string.h> #include <fstream> #include <conio.h> #include <stdio.h> #incl…

说说RDB和AOF

简介&#xff1a; 众所周知&#xff0c;redis是一个内存数据库&#xff0c;当机器重启后&#xff0c;内存中数据都会丢失。所以redis提供了两种持久化方式&#xff0c;即&#xff1a;RDB(保存一个时间点前的数据)和AOF(保存redis服务器端执行的每一条命令)。 RDB: RDB有两种…

【算法与数据结构】300、674、LeetCode最长递增子序列 最长连续递增序列

文章目录 一、300、最长递增子序列二、674、最长连续递增序列三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、300、最长递增子序列 思路分析&#xff1a; 第一步&#xff0c;动态数组的含义。 d p [ i ] dp[i] dp[i…

程序执行内存区域,堆栈使用及区别

文章目录 一、程序执行内存区域二、堆区详解三、栈内存与堆内存的区别总结 一、程序执行内存区域 **代码区&#xff1a;**当我们的程序被执行时&#xff0c;它会有一个加载准备的过程。其中函数及内部的流程结构代码指令会被放到代码区中&#xff0c;等待着被调用执行。 **常数…

【JS】基于React的Next.js环境配置与示例

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍基于React的Next.js环境配置与示例。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…