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! 

                                       

                                                                        给个三连再走嘛~  

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2. 路由 Vue-Router

目录 2.1 Vue-Router 介绍 2.2 路由配置 2.3 嵌套路由 Vue1&#xff1a;基础跟使用方式 2.1 Vue-Router 介绍 vue 属于单页面应用&#xff0c;所谓路由&#xff0c;就是根据浏览器路径不同&#xff0c;用不同的视图组件替换这个页面内容。 在vue应用中使用路由功能&#x…

JupyterLab 更换内核 使用 conda 虚拟环境

未有conda虚拟环境default先创建环境 conda create -n default python3.8 ipykernel已有conda虚拟环境default激活后安装ipykernel conda activate defaultpip install ipykernel将虚拟环境写入 jupyter notebook 的 kernel 中 python -m ipykernel install --user --name 虚…

SpringBoot security 安全认证(三)——自定义注解实现接口放行配置

背景&#xff1a;通过Security实现了安全管理&#xff0c;可以配置哪些接口可以无token直接访问。但一个麻烦就是每增加一个匿名访问接口时都要去修改SecurityConfig配置&#xff0c;从程序设计上讲是不太让人接受的。 本节内容&#xff1a;即是解决以上问题&#xff0c;增加一…

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

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

python2.7安装和添加环境变量

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、python的下载地址二、安装步骤1.双击安装包运行安装2.添加环境变量 三、验证是否安装成功总结 前言 最近要用到python,下载安装成功&#xff0c;添加环境变…

Java 数据结构 二叉树(二)红黑树

目录 数据结构图-树 简介 规则 旋转 重新着色 红黑树构建过程 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡&#xff0c;忘记了停下脚步&#xf…

华清远见嵌入式学习——春节作业——2.4日

作业要求&#xff1a; 编写程序实现二叉树的创建&#xff0c;三种遍历自己销毁 作业答案&#xff1a; 作业代码截图 作业代码效果图 作业代码 #include "myhead.h"// 定义二叉树节点结构体 struct Tree {int value; //编号(值)struct Tree* left; //左子树stru…

9隐藏登录用户(无需接触磁盘即可修改文件内容)_Linux_Rootkit.md

Xcellerator 密码学Linux其他逆向工程 文章目录 [Linux Rootkit 第 9 部分&#xff1a;隐藏登录用户&#xff08;无需接触磁盘即可修改文件内容&#xff09;](https://xcellerator.github.io/posts/linux_rootkits_09/)终端设备UTMP用户空间工具如何解析 UTMP&#xff1f;挂钩…

如何实现冻干机和产品全生命周期的验证和监测?

为什么冻干需要工艺优化和合规性 冻干是制药和生物技术产品的关键工艺&#xff0c;需要精确控制关键的温度和压力参数。通过遵守 GMP 和 FDA 合规性等监管准则&#xff0c;您可以生产出更高质量的产品&#xff0c;避免不必要的浪费&#xff0c;并缩短产品上市时间。 要想在冻干…

[linux] kolla-ansible 部署的openstack 修改mariadb默认端口号

kolla-ansible 部署前修改global.yml #freezer_database_backend: "mariadb" database_port: 9306 mariadb_port: 9306如果已经部署成功&#xff0c;直接修改配置文件里的端口号重启是没有用的&#xff0c;怀疑内部做了缓存&#xff0c;查看openstack 使用的memcach…

零基础学编程,从入门到精通,中文编程工具下载,时间选择构件用法

零基础学编程&#xff0c;从入门到精通&#xff0c;中文编程工具下载&#xff0c;时间选择构件用法 一、前言 编程工具下载及源码文件下载路径 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例源码文件下载可以点击最下方官网卡片——软件…

Unity类银河恶魔城学习记录1-11 PlayerPrimaryAttack P38

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Player.cs using System.Collections; using System.Collections.Generic…

基于spring boot实现邮箱发送和邮箱验证

目录 一、邮箱发送实现1. 开通邮箱服务2. 添加邮箱依赖3.添加配置4.添加邮箱通用类5. 测试类 二、邮箱验证实现1.添加依赖2. 添加配置3.添加controller4. 测试 项目地址: https://gitee.com/nssnail/springboot-email 一、邮箱发送实现 1. 开通邮箱服务 使用qq邮箱、163邮箱都…

《计算机网络简易速速上手小册》第10章:未来网络技术趋势(2024 最新版)

文章目录 10.1 边缘计算与网络设计 - 未来网络的速度与激情10.1.1 基础知识10.1.2 重点案例&#xff1a;使用 Python 实现边缘计算的实时视频分析准备工作Python 脚本示例 10.1.3 拓展案例1&#xff1a;智能交通系统Python 脚本示例 - 边缘计算设备上的交通流量分析 10.1.4 拓展…

vcruntime140_1.dll 文件缺失的解决方法,简单的修复方法分享

遇到 vcruntime140_1.dll 文件缺失是一种常见的问题&#xff0c;但无需过于担心&#xff0c;因为这类问题通常有多种解决办法。接下来&#xff0c;我会为大家详细介绍如何处理 vcruntime140_1.dll 文件缺失的情况&#xff0c;以及在解决过程中应该注意的重要事项。 一.vcrunti…

Python flask 模板详解

文章目录 1 概述1.1 模板简介1.2 templates 文件1.3 简单应用 2 模板语法2.1 for 循环2.2 if 判断 3 模板的继承3.1 格式要求3.2 实现示例3.3 复用父模板的内容&#xff1a;super 1 概述 1.1 模板简介 定义&#xff1a;定义好的 html 文件&#xff0c;用于快速开发 web 页面J…

04-OpenFeign-请求超时机制

基于 2021.0.1版本 具体有以下几种超时参数设置 以下仅限2021.0.1 以前的版本&#xff0c;高于该版本的spring cloud 已修复该问题 1、默认的超时时间 默认不生效 连接超时时间10秒、读超时时间60秒&#xff0c;源码在feign.Request.Options#Options()这个方法中 2、open…

在Linux中如何理解页表和进程地址

1、进程地址是进程读取资源的窗口 2、页表决定了进程真实拥有的资源情况 3、合理的对进程地址空间页表进行资源划分&#xff0c;就可以对进程的资源进行分类 这个过程应该如何去理解呢请看下面的图 我们知道程序被加载到进程中&#xff0c;会产生相应的PCB&#xff0c;并且…

【JavaScript 漫游】【006】数据类型 array

文章简介 本文为【JavaScript 漫游】专栏的第 006 篇文章&#xff0c;记录笔者在了解 JS 数据类型 array 中摘录的知识点。 数组的本质是对象属组的 length 属性for ... in 循环和数组的遍历数组的空位类数组对象 除了上述 5 个重要知识点&#xff0c;学习数组更为重要的是掌…

CSS transition(过渡效果)详解并附带示例

CSS过渡效果&#xff08;CSS transitions&#xff09;是一种在元素属性值发生变化时&#xff0c;通过指定过渡效果来实现平滑的动画效果的方法。通过定义起始状态和结束状态之间的过渡属性&#xff0c;可以使元素的变化更加流畅和可视化。 过渡效果的基本语法如下&#xff1a;…