[Linux]:信号(上)

news2024/9/20 16:40:48

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. 信号的引入

1.1 信号的概念

Linux系统中,信号(Signal)是一种软件中断机制,用于通知进程发生了特定的事件。信号可以由系统内核、其他进程或者进程自身发送。

我们可以通过指令kill -l参考所有信号:

信号的本质就是一个define定义的宏,其中131号信号是普通信号,3464号信号是实时信号,普通信号和实时信号各自都有31个。每一个信号与一个数字相对应,每个信号也都有特定的含义和默认的处理动作。例如,信号SIGINT(通常由用户按下ctrl + c产生)表示中断信号,默认情况下会导致进程终止。

其中需要注意的是:在Linux中,前台进程只能有一个,而后台进程可以为多个。一般而言,我们的bash进程作为我们的前台进程,而一旦我们执行一个可执行程序,这个可执行程序就会成为前台进程,而bash进程就会转为后台进程。但是我们如果在执行一个可执行程序时,在之后加一个&,此时的可执行程序就会由前台进程转换为后台进程。而前台进程与后台进程本质间区别就是前台进程可以从键盘获取数据,后台进程则不能。

比如我们运行一个后台进程,就无法通过ctrl + c终止进程,因为其无法从键盘读取数据。此时就只能通过kill指令直接杀死对应的进程。

1.2 信号的获取

我们可以通过指令man 7 signal查看信号的详细说明:

其中 TermCore 表示终止;Ign 标记忽略;Cont 表示继续;Stop 表示暂停。我们早在进程等待时就知道,waitwaitpid的参数status本质就是一个位图结构,其低16比特位当中,高8位表示进程的退出状态,即退出码。进程若是被信号所杀,则低7位表示终止信号,而第8位比特位是core dump标志。

pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

画板

其中 core dump 标志就是用来区分 TermCore 的。云服务器的 Core dump 功能默认是关闭的,但我们可以通过指令ulimit -a 指令来查看当前系统的所有资源限制。

我们可以通过指令ulimit -c size,去设置它的大小,如果 size > 0 就表示开启 Core dump 功能。

其中Term对应的core dump标志位是 0,表示正常终止;Core对应的core dump标志位是 1,表示异常终止。我们可以在程序中,通过位运算status>>7&1来获取对应的core dump标志。

打开系统的core dump功能后,一旦进程出现异常,操作系统会将进程在内存中的运行信息转储到进程的当前目录中,形成core.pid文件,这一过程被称作核心转储。core.pid文件中详细记录了程序的异常原因,可以直接帮我们定位到出错行。

比如如下这段代码:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
    int a = 10;
    int b = 0;
    a /= b;
    return 0;
}

既然core dump可以帮助我们定位错误信息,那么我们为什么要将其关闭呢?那是因为如果每次产生错误信息都形成core.pid文件的话,系统可能产生大量文件,而迫使操作系统挂掉,为了避免这种情况,一般而言我们并不建议开启该功能。

1.3 signal函数

当一个信号被发送给一个进程时,进程可以采取以下几种方式来处理信号:

  1. 忽略信号:进程可以选择忽略某些信号,即不对信号做出任何反应。但并不是所有信号都可以被忽略,例如 SIGKILL SIGSTOP 信号不能被忽略。
  2. 捕获信号:进程可以注册一个信号处理函数,当接收到特定信号时,就会执行这个函数。通过这种方式,进程可以在接收到信号时执行自定义的处理逻辑。
  3. 执行默认动作:如果进程没有显式地忽略或捕获信号,那么它将执行信号的默认动作。默认动作通常是终止进程、停止进程、继续进程等。

接下来我们介绍一个函数signal,其可以设置进程对某个信号的自定义捕捉方法:即当进程收到 signum 信号的时候,去执行 handler 方法。

  1. 函数原型:
  • typedef void (*sighandler_t)(int);
  • sighandler_t signal(int signum, sighandler_t handler);
  1. 参数:
  • signum:是一个整数,表示要处理的信号编号。
  • handler:是一个函数指针,指向一个信号处理函数。这个信号处理函数接受一个整数参数(即接收到的信号编号),并且没有返回值(void)。可以是以下几种值:
    • SIG_DFL:表示默认的信号处理动作。
    • SIG_IGN:表示忽略该信号。
    • 自定义的信号处理函数指针,用于处理特定信号。

例如,下面的代码中将2号信号进行了捕捉,当该进程运行起来后,若该进程收到了2号信号就会打印出对应的信号编码。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handler(int sign)
{
    printf("get a signal :%d\n",sign);
}
int main()
{
    signal(2,handler);
    while(1)
    {
        printf("hello world!\n");
        sleep(1);
    }
    return 0;
}

其中前台进程在运行过程中,用户随时可能按下Ctrl+C而产生一个信号,也就是说该进程的代码执行到任何地方都可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步的。

2. 信号的产生

在我们操作系统中,信号的产生方式有许多,总体归纳来说有四种。

2.1 终端按键

其中我们通过键盘快捷键直接向我们的进程发出信号的方式非常常见,其中较为我们常用的有:

组合键功能
Ctrl+C向进程发出SIGINT信号,终止进程。
Ctrl+\向进程发出SIGQUIT信号,终止进程。
Ctrl+Z向进程发送SIGTSTP信号,暂停进程的执行。

2.2 系统接口

我们也可以通过操作系统为我们提供的接口对进程发送对应的信号。

其中较为常用的一个接口为kill,其具体用法如下:

  1. 函数原型:int kill(pid_t pid, int sig);
  2. 参数:pid对应要发送信号进程的pidsig表示发送的信号种类。
  3. 返回值:如果成功,返回值为 0。否则,返回值为 -1

例如:下面这段代码,我们可以对指定进程发送一个SIGKILL信号,正常终止该进程。

int main()
{
    int cnt = 0;
    while(1)
    {
        printf("hello world!\n");
        sleep(1);
        ++cnt;
        if(cnt == 5)
        {
            kill(getpid(),SIGKILL);
        }
    }
    return 0;
}

接下来我们再来介绍两个接口:raiseabort

int raise(int sig);
void abort(void);

raise函数用于给当前进程发送sig号信号,而abort函数相当于给当前进程发送SIGABRT信号,使当前进程异常终止。

abortexit函数同样是终止进程,它们之间有什么区别吗?

首先明确abort函数和exit函数的不同作用。abort函数的作用是异常终止进程,它本质上是通过向当前进程发送SIGABRT信号来实现这一目的。而exit函数的作用是正常终止进程。

需要注意的是,使用exit函数终止进程可能会失败,因为在某些复杂的程序运行环境中,可能存在一些因素干扰正常的进程终止流程。然而,使用abort函数终止进程通常被认为总是成功的,这是由于其通过发送特定信号强制终止进程,一般情况下进程很难忽略该信号而继续运行。

2.3 软件条件

在我们前面学习管道通信时,就知道如果进程将读端关闭,而写端进程还一直向管道写入数据,那么此时写端进程就会收到SIGPIPE信号进而被操作系统终止。SIGPIPE就是一种典型的因为软件异常而产生的信号。

例如,下面代码,创建匿名管道进行父子进程之间的通信,其中父进程去读取数据,子进程去写入数据,但是一开始将父进程的读端关闭了,那么此时子进程在向管道写入数据时就会收到SIGPIPE信号,进而被终止。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    int fd[2]={0};
    if(pipe(fd)<0)
    {
        perror("pipe:");
        return 1;
    }
    pid_t id = fork();
    if(id ==0 )
    {
        //child -> write
        close(fd[0]);
        char*msg = "hello father, i am child...";
        while(1)
        {
            write(fd[1],msg,strlen(msg));
            sleep(1);
        }
        close(fd[1]);
        exit(0);
    }
    // father -> read
    close(fd[1]);
    close(fd[0]);
    int status = 0;
    waitpid(id,&status,0);
    printf("child get a signal :%d\n",status&0x7f);
    return 0;
}

我们能够通过alarm函数,设定一个闹钟,倒计时完毕向我们的进程发送SLGALRM信号,其具体用法如下:

  1. 函数原型:unsigned int alarm(unsigned int seconds);
  2. 参数:seconds表示倒计时的秒数。
  3. 返回值:如果调用alarm函数前,进程已经设置了闹钟,则返回上一个闹钟时间的剩余时间,并且本次闹钟的设置会覆盖上一次闹钟的设置。如果调用alarm函数前,进程没有设置闹钟,则返回值为0。

例如下面这段代码,我们首先对SLGALRM信号进行捕捉,并给出我们的自定义方法,然后5秒后调用alarm函数。

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
void handler(int sign)
{
    printf("get a signal:%d\n",sign);
    exit(1);
}
int main()
{
    signal(SIGALRM,handler);
    alarm(5);
    while(1)
    {
        printf("hello wrold!\n");
        sleep(1);
    }
    return 0;
}

2.4 硬件异常

当程序出现除 0、野指针、越界等错误时,程序会崩溃,本质是进程在运行中收到操作系统发来的信号而被终止。 这些发送的信号都是由硬件异常产生的。

比如下面这段代码,进行了对空指针的解引用,那么其到底是如何被操作系统识别的呢?

#include<stdio.h>
int main()
{
  int *p = NULL;
  *p = 3;//对空指针解引用。
  return 0;
}

首先我们知道,当我们要访问一个变量时,进程控制块task_struct一定要会经过页表的映射,将虚拟地址转换成物理地址,然后才能进行相应的访问操作。

画板

而页表属于一种软件映射关系,在从虚拟地址到物理地址映射过程中,有一个硬件单元叫做 MMU(内存管理单元),它是负责处理 CPU 的内存访问请求的计算机硬件。如今,MMU 已集成到 CPU 当中。虽然映射工作原本不是由 CPU 做而是由 MMU做,但现在其与 CPU 的紧密结合使得整个内存访问过程更加高效。

当进行虚拟地址到物理地址的映射时,先将页表左侧的虚拟地址提供给 MMUMMU会计算出对应的物理地址,随后通过这个物理地址进行相应的访问。

由于 MMU 是硬件单元,所以它有相应的状态信息。当要访问不属于我们的虚拟地址时,MMU 在进行虚拟地址到物理地址的转换时会出现错误,并将对应的错误写入到自己的状态信息当中。此时,硬件异常,硬件上的信息会立马被操作系统识别到,进而向对应进程发送 SIGSEGV 信号。

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

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

相关文章

在泰国旅游不会口语怎么办?求推荐翻译软件!!!

如果在泰国旅游时遇到语言障碍&#xff0c;可以采取以下措施&#xff1a;学习一些基础的泰语短语&#xff0c;使用翻译应用程序&#xff0c;携带翻译卡片&#xff0c;利用身体语言&#xff0c;参加有导游的旅行团&#xff0c;选择提供中文服务的酒店和旅行社&#xff0c;使用地…

【leetcode】字典 哈希表习题

1.两数之和&#xff08;查找表法-哈希表&#xff09; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次…

【人工智能学习笔记】7_智能语音技术基础

智能语音技术概述 智能语音技术通过对语音进行分析、理解和合成,是计算机设备实现“能听会说”、具备自然语音交流的技术能力。其涉及的范围主要有: 语音合成技术语音识别技术语音测评技术语音降噪与增强技术…智能语音技术的研究内容 智能语音技术的研究难点 智能语音技术…

旅行社区应该如何规划?

近年来&#xff0c;旅游行业逐渐恢复&#xff0c;包括微度假、精致露营、康养旅游、乡村民宿等旅游模式。用户旅游支出、旅游人次逐渐恢复&#xff0c;旅游收入仍待提升。 那么旅游社区应该如何搭建&#xff0c;内容如何规划呢&#xff1f; 我们了解到&#xff0c;很多旅游网…

哪个快?用300万个图斑测试ArcGIS Pro的成对叠加与经典叠加

​​​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 在使用ArcGIS Pro的过程中&#xff0c;很多朋友发现&#xff0c;Pro有个成对叠加工具集。很多…

PSG3D V2024 注册机 蓝天大数据三维测绘系统

PSG3D2024三维测绘系统是基于 AutoCAD 平台开发的&#xff0c;以“智能、 专业、高效、易学”为设计目标开发的全新一代地理数据采集、编辑 和联动的专业软件。 系统集成了三维测图、坐标转换、地形处理、数字地模、无人机 辅助、不动产调查、土地确权、部件普查和数据转换等专…

数字产业中心:技术赋能产业,如何重塑行业格局!

在数字化浪潮的推动下&#xff0c;数字产业中心正逐步成为推动经济转型升级的重要引擎。这里&#xff0c;技术不仅仅是工具&#xff0c;更是重塑行业格局、引领未来发展的核心力量。 一、技术融合创新&#xff0c;打破传统边界 数字产业中心通过云计算、大数据、人工智能等前沿…

【Python】练习:控制语句(二)第1关

第1关&#xff1a;分支结构基础实训 第一题第二题第三题第四题&#xff08;※&#xff09;第五题&#xff08;※&#xff09;第六题第七题 第一题 #第一题 for temp in [-280, -100, 0, 20, 120, 200]:#请在下面编写代码# ********** Begin ********** #if temp>-273.15:F9/…

【Git 操作】Git 的基本操作

文章目录 1. Git 的配置2. 工作区、暂存区、版本库 1. Git 的配置 &#x1f427;①首先要新建一个目录&#xff0c;该目录用于放项目代码&#xff0c;在该目录下执行git init命令&#xff0c;用于创建一个 Git的本地仓库。 .git ⽬录是 Git 来跟踪管理仓库的。&#x1f427;②…

原生+jquery写自动消失的提示框

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>自动消失消息提示</title> <style>/…

linux内核 devtmpfs介绍

文章目录 概要整体架构流程技术细节 概要 提示&#xff1a;这里可以添加技术概要 linux内核中 devtmpfs实现介绍 内核版本&#xff1a;5.10 Devtmpfs在Linux中是一个特殊的设备文件系统&#xff0c;主要用来linux内核中加速启动过程和管理设备节点。高版本的linux基本都是使用…

25届计算机专业毕设选题推荐-基于python的二手电子设备交易平台【源码+文档+讲解】

&#x1f496;&#x1f525;作者主页&#xff1a;毕设木哥 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; 实战项目 文章目录 实战项目 一、基于python的二手电子设备交…

【渗透测试】——VulnHub靶机渗透实战 | HA:Joker

&#x1f4d6; 前言&#xff1a;Vulnhub 是一个漏洞靶场平台&#xff0c;里面含有大量的靶场镜像&#xff0c;只需要下载虚拟机镜像&#xff0c;导入 VMWare 或者 VirtualBox 即可启动靶场。本文将从环境搭建、端口扫描、目录扫描到信息提取和突破8080端口&#xff0c;尽可能排…

Cesium Shader 广告牌纹理动画

Cesium Shader 广告牌纹理动画 Cesium 在广告牌, 自定义shader实现播放spritesheet. 图片资源: https://www.codeandweb.com/free-sprite-sheet-packer Cesium Shader 广告牌纹理动画

MySQL高阶1843-可疑银行账户

目录 题目 准备数据 ​分析数据 实现 总结 题目 如果一个账户在 连续两个及以上 月份的 总收入 超过最大收入&#xff08;max_income&#xff09;&#xff0c;那么认为这个账户 可疑。 账户当月 总收入 是当月存入资金总数&#xff08;即 transactions 表中 type 字段的…

Vivado FIR IP 详解 (一)

FIR滤波器是数字信号处理中常用的滤波器&#xff0c;除了通过Verilog代码自己实现外&#xff0c;Vivado提供了一个FIR滤波器 IP&#xff0c;可以直接调用。 一、什么是 Vivado FIR IP 核 FIR&#xff08;Finite Impulse Response&#xff09;滤波器即有限长单位冲激响应滤波器…

兔子检测系统源码分享

兔子检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

安卓13设置动态显示隐藏第一页的某一项 动态显示隐藏无障碍 android13设置动态显示隐藏第一页的某一项

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改4.1修改方法14.2修改方法25.编译6.彩蛋1.前言 有时候,我们的设置里面显示的信息,需要根据不同的情况显示不同的信息,例如,动态的显示或者隐藏 “无障碍” 这一项。 2.问题分析 像这个问题…

【经验帖】脏读和不可重复读的概念及影响

脏读和不可重复读是数据库事务并发执行时可能出现的两种数据一致性问题&#xff0c;它们对数据的一致性和完整性有着显著的影响。以下是脏读和不可重复读的具体影响&#xff1a; 脏读的影响 脏读发生在一个事务读取了另一个事务未提交的数据时。由于这些数据尚未被提交&#x…

python机器人编程——用手机web远程视频监控并控制小车驾驶(上篇vrep仿真)

目录 一、前言二、技术架构三、设备端实现四、服务控制端实现&#xff08;1&#xff09;摄像头服务模块&#xff08;2&#xff09;web服务器 五、web端实现&#xff08;1&#xff09;视频显示&#xff08;2&#xff09;驾驶盘的实现&#xff08;3&#xff09;心跳 六、总结七、…