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

news2024/7/30 0:46:39

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

                                              ♈️今日夜电波: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.coloradmin.cn/o/1432015.html

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

相关文章

32ADC模数转换器&AD单通道&多通道

目录 一.简介 二.逐次逼近法​编辑 三.结构框图 四.小tips (1)转换模式 &#xff08;2&#xff09;触发控制 &#xff08;3&#xff09;数据对齐 &#xff08;4&#xff09;转换时间 &#xff08;5&#xff09;校准 &#xff08;6&#xff09;硬件电路 五.相关函数 …

Java语法学习IO流

Java语法学习IO流 大纲 文件IO流 具体案例 1. 文件 基本介绍 创建文件 第一种&#xff1a; public static void main(String[] args) {String filePathName "d:\\news1.txt";File file new File(filePathName);try {file.createNewFile();} catch (IOExceptio…

vulhub中Apache Druid 代码执行漏洞复现(CVE-2021-25646)

Apache Druid是一个开源的分布式数据存储。 Apache Druid包括执行嵌入在各种类型请求中的用户提供的JavaScript代码的能力。这个功能是为了在可信环境下使用&#xff0c;并且默认是禁用的。然而&#xff0c;在Druid 0.20.0及以前的版本中&#xff0c;攻击者可以通过发送一个恶…

2018 年全国职业院校技能大赛高职组“信息安全管理与评估”赛项任务书(笔记解析)

1. 网络拓扑图 2. IP 地址规划表 3. 设备初始化信息 阶段一 任务 1:网络平台搭建 1、根据网络拓扑图所示,按照 IP 地址参数表,对 WAF 的名称、各接口 IP 地址 进行配置。 2、根据网络拓扑图所示,按照 IP 地址参数表,对 DCRS 的名称、各接口 IP 地址 进行配置。 3、根据网…

C++项目 -- 高并发内存池(二)Thread Cache

C项目 – 高并发内存池&#xff08;二&#xff09;Thread Cache 文章目录 C项目 -- 高并发内存池&#xff08;二&#xff09;Thread Cache一、高并发内存池整体框架设计二、thread cache设计1.整体设计2.thread cache哈希桶映射规则3.TLS无锁访问4.thread cache代码 一、高并发…

CCF迎来新风采:揭晓2024-2026年度执行机构负责人名单!

会议之眼 快讯 中国计算机学会&#xff08;CCF&#xff09;成立于1962年&#xff0c;是一家全国性学会&#xff0c;拥有独立社团法人地位&#xff0c;同时是中国科学技术协会的会员单位。作为中国计算机及相关领域的学术团体&#xff0c;CCF的宗旨在于为该领域专业人士的学术和…

C

extern int a; //同一个项目声明 int r a > b ? a : b; 错误 scanf 不输入‘\n’,getchar()输入\n; printf()返回值 0次 system("cls"); 可以调用命令行函数 time(NULL)时间戳 srand((unsigned)time(NULL)); //随机数种子 int rev rand()%1001; //随…

Linux---yum命令详解

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.概念2.yum的配置信…

Open CASCADE学习|拓扑变换

目录 平移变换 旋转变换 组合变换 通用变换 平移变换 TopoDS_Shape out;gp_Trsf theTransformation;gp_Vec theVectorOfTranslation(0., 0.125 / 2, 0.);theTransformation.SetTranslation(theVectorOfTranslation);BRepBuilderAPI_Transform myBRepTransformation(out, th…

一篇文章了解区分指针数组,数组指针,函数指针,链表。

最近在学习指针&#xff0c;发现指针有这许多的知识&#xff0c;其中的奥妙还很多&#xff0c;需要学习的也很多&#xff0c;今天那我就将标题中的有关指针知识&#xff0c;即指针数组&#xff0c;数组指针&#xff0c;函数指针&#xff0c;给捋清楚这些知识点&#xff0c;区分…

【Linux取经路】进程控制——程序替换

文章目录 一、单进程版程序替换看现象二、程序替换的基本原理三、程序替换接口学习3.1 替换自己写的可执行程序3.2 第三个参数 envp 验证四、结语一、单进程版程序替换看现象 #include <stdio.h> #

【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了......

目录 【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了...... 龙年专属测试鼠标寿命小游戏用Python给大家半年了贪吃龙游戏 文章所属专区 码农新闻 欢迎各位编程大佬&#xff0c;技术达人&#xff0c;以及对编程充满热情的朋友们&#xff0c;来到我们的程…

Spring Boot + flowable 快速实现工作流

背景 使用flowable自带的flowable-ui制作流程图 使用springboot开发流程使用的接口完成流程的业务功能 文章来源&#xff1a;https://blog.csdn.net/zhan107876/article/details/120815560 一、flowable-ui部署运行 flowable-6.6.0 运行 官方demo 参考文档&#xff1a; htt…

SpringBoot整合Flowable最新教程(二)启动流程

介绍 文章主要从SpringBoot整合Flowable讲起&#xff0c;关于Flowable是什么&#xff1f;数据库表解读以及操作的Service请查看SpringBoot整合Flowable最新教程&#xff08;一&#xff09;&#xff1b;   其他说明&#xff1a;Springboot版本是2.6.13&#xff0c;java版本是1…

4. 树(二叉树、二叉查找树/二叉排序树/二叉搜索树、平衡二叉树、平衡二叉B树/红黑树)

树 1. 二叉树1.1 概述1.2 特点1.3 二叉树遍历方式1.3.1 前序遍历(先序遍历)1.3.2 中序遍历1.3.3 后序遍历1.3.4 层序遍历 2. 二叉查找树&#xff08;二叉排序树、二叉搜索树&#xff09;2.1 概述2.2 特点 3. 平衡二叉树3.1 概述3.2 特点3.3 旋转3.3.1 左旋3.3.2 右旋 3.4 平衡二…

指针的学习2

目录 数组名的理解 使用指针访问数组 一维数组传参的本质 冒泡排序 二级指针 指针数组 指针数组模拟二维数组 数组名的理解 数组名是数组首元素的地址 例外&#xff1a; sizeof(数组名),sizeof中单独放数组名&#xff0c;这里的数组名表示整个数组&#xff0c;计算的…

EasyCVR智能视频监控平台云台降低延迟小tips

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

彻底学会系列:一、机器学习之线性回归

1.基本概念 线性回归&#xff1a; 有监督学习的一种算法。主要关注多个因变量和一个目标变量之间的关系。 因变量&#xff1a; 影响目标变量的因素&#xff1a; X 1 , X 2 . . . X_1, X_2... X1​,X2​... &#xff0c;连续值或离散值。 目标变量&#xff1a; 需要预测的值: t…

智慧未来已至:人工智能与数字孪生共筑城市新纪元

随着科技的飞速发展&#xff0c;人工智能与数字孪生技术正逐步成为智慧城市建设的核心驱动力。 这两项技术的结合&#xff0c;不仅将彻底改变城市的传统面貌&#xff0c;更将引领我们走向一个更加高效、便捷、绿色的未来。 一、智慧城市的新内涵 智慧城市&#xff0c;是指在城…

DDoS攻击:分布式拒绝服务攻击的威胁与对策

DDoS攻击&#xff1a;分布式拒绝服务攻击的威胁与对策 随着互联网的快速发展&#xff0c;网络安全威胁也在不断增加。其中&#xff0c;分布式拒绝服务攻击&#xff08;DDoS&#xff09;是一种常见且具有破坏性的攻击方式&#xff0c;给个人用户、企业和组织的网络基础设施带来了…