【Linux初阶】信号入门 | 信号基本概念+信号产生+核心转储

news2024/11/25 11:28:07

🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:Linux信号的基本概念(生活信号、技术信号、信号生命周期、信号的保存位置和发送本质),信号的产生(四种方式、一个系统调用接口)
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-


文章目录

  • ☀️一、Linux信号的基本概念
    • 🌻1.生活中的信号
    • 🌻2.技术应用角度的信号
    • 🌻3.信号的生命周期
    • 🌻4.用kill -l命令可以察看系统定义的信号列表
    • 🌻5.信号的保存位置 & 信号发送的本质
      • ⚡️(1)信号的保存位置
      • ⚡️(2)信号发送的本质
  • ☀️二、信号的产生
    • 🌻1.按键产生信号
    • 🌻2.系统调用接口 - signal(信号捕捉+方法实现)
    • 🌻3.系统调用向进程发送信号
      • ⚡️(1)kill系统调用
      • ⚡️(2)raise & abort 调用
    • 🌻4.硬件异常产生信号
      • ⚡️(1)除0问题
      • ⚡️(2)野指针问题
    • 🌻5.由软件条件产生信号
      • ⚡️(1)闹钟简介
      • ⚡️(2)闹钟管理
  • ☀️三、知识归纳
  • ☀️四、信号的核心转储
  • 结语


☀️一、Linux信号的基本概念

🌻1.生活中的信号

  • 我们生活中等快递小哥取快递、红绿灯、闹钟、手机消息等,我们都可以将他们理解为一种信号。
  • 接收快递的前置条件是你知道怎么处理快递,也就是你能“识别快递”。
  • 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  • 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”。
  • 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动作(幸福的打开快递,使用商品)2.执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)。
  • 快递到来的整个过程,对你来讲是异步的,即信号可能随时产生,但是信号到来时我么不一定要立即处理,你可能做着更重要的事。

🌻2.技术应用角度的信号

我们可以将上面的概念迁移到我们的计算机信号学习中:

首先这里要补充一个共识,信号是给进程发的。

  • 进程如何识别信号?认识+动作
  • 为什么进程有能力认识信号?进程是程序员编码完成的,具有逻辑和属性的集合,也就是说进程有对应的逻辑和属性来认识和辨别信号。
  • 当收到信号之后,进程可能在执行其他更重要的代码,所以信号不一定被立即处理
  • 为了以后能在合适的时间处理信号,进程本身具有对于信号的保存能力
  • 进程在处理信号的时候,通常有三种动作:默认、自定义、忽略,此时我们称信号被捕捉

🌻3.信号的生命周期

  • 信号的生命周期分为 4个阶段:预备、信号产生、信号保存、信号处理。

在这里插入图片描述

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

使用指令 kill -l,我们可以发现系统为我们提供的信号有 64种,其中 [1, 31]我们称为普通信号,[32, 64]为实时信号,我们主要学习的是普通信号。

在这里插入图片描述

🌻5.信号的保存位置 & 信号发送的本质

⚡️(1)信号的保存位置

我们知道,信号是要发送给进程的,而进程需要保存信号,那么信号被保存在哪里呢?信号被保存在进程的 task_struct中。

在进程的 task_struct中保存有 unsigned int signal这样一个数据,它代表一个整数,或者说信号的位图。我们知道 unsigned int类型的整数由 32个比特位组成,而我们的普通信号 [1, 31]只有31个,也就是说我们可以使用比特位的位置代表信号编号

我们还可以用比特位的内容,代表是否收到对应的信号,0为没有,1为有
在这里插入图片描述

⚡️(2)信号发送的本质

  • 通过对信号保存位置的学习,我们不难理解,其实信号发送的本质就是:修改进程内部PCB中的信号位图
  • 进程PCB是内核维护的结构对象,也就是说,我们以后无论学习多少种信号发送方式,本质上都是通过 OS给目标进程发送信号。
  • 为了满足上层用户的需求,操作系统一定会给我们提供发送信号处理信号的系统调用接口。
  • kill 命令的底层一定调用了对应的系统调用。

☀️二、信号的产生

🌻1.按键产生信号

  • 用户输入命令,在Shell下启动一个前台进程。用户按下Ctrl+C ,这个按键输入会被OS获取,解释成信号,发送给目标前台进程。前台进程因为收到信号,进而引起进程退出。
  • 其中,Ctrl+C会被 OS解释成 2号信号(SIGINT),进程收到该信号后的默认动作为终止进程。
[root@localhost code_test]$ cat sig.c 
#include <stdio.h>
int main()
{
	while(1){
		printf("I am a process, I am waiting signal!\n");
		sleep(1);
	}
}

[root@localhost code_test]$ ./sig 
I am a process, I am waiting signal!
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C
[root@localhost code_test]$

总结:我们的按键可以被OS解释为信号(如:Ctrl+C会被 OS解释成 2号信号)。

———— 我是一条知识分割线 ————

🌻2.系统调用接口 - signal(信号捕捉+方法实现)

  • 当我们想捕捉一个信号,自定义实现相应的动作,我们可以使用 signal系统调用接口
  • 我们可以用 man signal指令查看对应的手册。
  • 使用 signal 系统调用接口,当收到某个信号(signum),会自动帮我们调用一个回调函数(handler),该回调函数返回值是 void,参数是 int。
  • 【注意】signal函数仅仅是设置了对信号的捕捉方法,需要进程收到对应的信号,才会调用函数。

在这里插入图片描述

  • 代码运行起来之后,如果我们给对应的进程发送 2号信号(Ctrl+C/kill -2 id),它会自动执行对应的 handler函数。
#include <iostream>
#include <unistd.h>
#include <signal.h>

void handler(int signo)
{
    std::cout << "进程捕捉到了一个信号,信号编号是: " << signo << std::endl;
    // exit(0);
}

int main()
{
    // 这里是signal函数的调用,并不是handler的调用
    /// 仅仅是设置了对2号信号的捕捉方法,并不代表该方法被调用了
    // 一般这个方法不会执行,除非收到对应的信号!
    signal(2, handler);

    while(true)
    {
        std::cout << "我是一个进程: " << getpid() << std::endl;
        sleep(1);
    }
}

总结:我们可以用 signal系统调用接口捕捉信号,并在捕捉到对应信号让进程执行特定的方法代码。

注意:对于某些恶意进程,我们可以用 kill -9 pid管理员信号将对应进程杀死。9号信号无法被捕捉,因此9号信号可以杀掉所有异常进程。

———— 我是一条知识分割线 ————

🌻3.系统调用向进程发送信号

⚡️(1)kill系统调用

  • kill()可以向任意进程发送任意信号。
  • kill系统调用函数,手册信息如下,返回值:成功为1,失败为-1。

在这里插入图片描述

我们平时使用的 kill命令,实际上底层使用了 kill系统调用函数。

  • makefile文件
.PHONY:all
all:mysignal mytest

mytest:mytest.cc
	g++ -o $@ $^ -std=c++11

mysignal:mysignal.cc
	g++ -o $@ $^ -std=c++11 -g

clean:
	rm -f mysignal mytest
  • mysignal.cc
#include <iostream>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

using namespace std;

static void Usage(const std::string& proc)
{
    std::cout << "\nUsage: " << proc << " pid signo\n"
        << std::endl;
}

// ./myprocess pid signo
int main(int argc, char* argv[])
{
    // 2. 系统调用向目标进程发送信号
    //kill系统调用函数
     if(argc != 3)
     {
         Usage(argv[0]);
         exit(1);
     }
    pid_t pid = atoi(argv[1]);
    int signo = atoi(argv[2]);
    int n = kill(pid, signo);
    if (n != 0)
    {
        perror("kill");
    }

}
  • mytest.cc
#include <iostream>
#include <sys/types.h>
#include <unistd.h>

//我写了一个将来会一直运行的程序,用来进行后续的测试
int main()
{
    while(true)
    {
        std::cout << "我是一个正在运行的进程,pid: " << getpid() << std::endl;
        sleep(1);
    }
}
  • 运行结果

在这里插入图片描述

⚡️(2)raise & abort 调用

  • raise & abort 实际上是在 kill系统调用的基础上做了相应的封装。
  • raise() 给自己 发送 任意信号【= kill(getpid(), 任意信号)】。
  • abort() 给自己 发送 指定的信号SIGABRT(6号信号), 【= kill(getpid(), SIGABRT)】。

关于信号处理的行为的理解:有很多的情况,进程收到大部分的信号,默认处理动作都是终止进程。

信号的意义:信号的不同,代表不同的事件,但是对事件发生之后的处理动作可以一样!

int main(int argc, char* argv[])
{
    // kill()可以想任意进程发送任意信号
    // raise() 给自己 发送 任意信号kill(getpid(), 任意信号)
    // abort() 给自己 发送 指定的信号SIGABRT, kill(getpid(), SIGABRT)
    // 关于信号处理的行为的理解:有很多的情况,进程收到大部分的信号,默认处理动作都是终止进程
    // 信号的意义:信号的不同,代表不同的事件,但是对事件发生之后的处理动作可以一样!
     int cnt = 0;
     while(cnt <= 10)
     {
         printf("cnt: %d, pid: %d\n", cnt++, getpid());
         sleep(1);
           if(cnt >= 5) abort(); // kill(getpid(), signo)
         // if(cnt >= 5) raise(9); // kill(getpid(), signo)
     }
}

总结:我们可以使用kill系统调用,raise & abort 调用向进程发送信号

🌻4.硬件异常产生信号

当我们运行代码时,如果操作系统识别到了异常,会向进程发送了特定的信号,使进程终止。

下面我举两个例子帮助大家理解:

⚡️(1)除0问题

CPU内部存在一个状态寄存器,以下述代码为例,当CPU在运行过程中,发现运算结果是没有意义的时候,会将状态寄存器的标志位从0置1,而后操作系统发现后,将8号信号发送给对应的进程,进程获取到8号信号之后就自行终止了。

int a = 10;

a /= 0;

至此,我们就知道了,当我们获取到 8号信号(SIGFPE)时,代码中存在 除0错误

⚡️(2)野指针问题

当代码出现野指针并运行的时候,会导致虚拟地址到物理地址转化过程中的一个名为 MMU硬件的报错,进而被操作系统识别到报错,而后操作系统会向进程发送 11号信号,使进程终止。

[root@localhost code_test]$ cat sig.c
#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
    printf("catch a sig : %d\n", sig);
}
int main()
{
    signal(SIGSEGV, handler);
    sleep(1);
    int* p = NULL;
    *p = 100;
    while (1);
    return 0;
}
[root@localhost code_test]$ ./sig
catch a sig : 11
catch a sig : 11
catch a sig : 11

至此,我们就知道了,当我们获取到 11号信号(SIGSEGV)时,代码中存在 段错误

总结:虽然我们知道我们收到信号之后操作系统的大部分处理方式为终止进程,但是我们仍旧可以根据收到信号的不同来判断进程报错的原因是什么,比如收到 8号信号为除 0错误、11号信号为 段错误。

我们可以通过下述指令查看不同信号产生原因处理办法

man 7 signal

在这里插入图片描述

———— 我是一条知识分割线 ————

🌻5.由软件条件产生信号

SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了,即读端关闭但是写端仍旧不断写入,OS会产生 SIGPIP(13号)信号。本节主要介绍alarm函数SIGALRM信号

在这里插入图片描述

⚡️(1)闹钟简介

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

代码示例(统计1S左右,我们的计算机能够将数据累计多少次!):

int cnt = 0;
void catchSig(int signo)
{
    std::cout << "获取到一个信号,信号编号是: " << std::endl;
    // exit(1);
    //alarm(1);	//可以重复设定闹钟
}
int main()
{
    // 4. 软件条件 -- "闹钟"其实就是用软件实现的
    // IO其实很慢
    //统计1S左右,我们的计算机能够将数据累计多少次!
    signal(SIGALRM, catchSig);
    alarm(1);
    while (true)
    {
        cnt++;
    }
}

总结:1.如果代码需要 IO,将会减慢计算机的运行速度;2.闹钟可用于在特定时间后向进程发送特定信号(SIGALRM信号),闹钟使用的是alarm函数。

⚡️(2)闹钟管理

在这里插入图片描述

总结:OS会定时检测堆结构堆顶的闹钟,如果闹钟超时,会向该闹钟对应的进程发送信号,然后检测下一个闹钟。


☀️三、知识归纳

  • 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者。
  • 信号的处理是否是立即处理的?在合适的时候
  • 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?信号被保存在进程的 task_struct中。
  • 一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?能,OS设计者已经将对应的处理方法设定好了。
  • 如何理解OS向进程发送信号?本质:修改进程的PCB位图。

☀️四、信号的核心转储

核心转储:当进程出现异常时,在对应时间内,将进程的有效数据转储到磁盘中。

在上面的文章中我们提到,我们可以通过指令查看信号处理方法:

man 7 signal

在这里插入图片描述
其中,Action中的 Term代表进程是正常结束的,也就是说 OS不会给我们做额外的工作。而 Core代表进程终止,也就是说除了终止进程,OS还要做额外的工作(核心转储)。

当我们在云服务器上操作时,Core所做的额外工作我们不能明显看到。因为云服务器默认关闭了核心转储(core file选项)。

———— 我是一条知识分割线 ————

云服务器可以通过指令查看是否开启核心转储打开核心转储

ulimit -a	//查看
ulimit -c 1024	//打开核心转储

在这里插入图片描述

核心转储发生后,会自动生成一个带有原 pid后缀的文件。

为什么要有核心转储?支持调试(迅速找到错误原因和位置)。
支持方法:gdb调试 + core-file XXX。

在这里插入图片描述

Term和Core差别总结:只有 Core终止的进程支持核心转储,调试查找错误。Term则是正常杀死进程,不支持额外操作。


结语

🌹🌹 【Linux初阶】信号入门 | 信号基本概念+信号产生+核心转储 的知识大概就讲到这里啦,博主后续会继续更新更多C++ 和 Linux的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

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

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

相关文章

Pytorch-MLP-CIFAR10

文章目录 model.pymain.py参数设置注意事项运行图 model.py import torch.nn as nn import torch.nn.functional as F import torch.nn.init as initclass MLP_cls(nn.Module):def __init__(self,in_dim3*32*32):super(MLP_cls,self).__init__()self.lin1 nn.Linear(in_dim,1…

RFID自动识别技术在数控工具系统的应用

RFID是一种自动识别技术&#xff0c;最早是应用在二战中进行敌我侦察机的识别&#xff0c;但是随着民用通信技术的放开&#xff0c;近年来网络通信技术以及信息安全技术都取得了重大的发展&#xff0c;RFID技术也逐渐在民用领域应用。 RFID自动识别技术在数控工具系统的应用 1、…

浅谈PDM与MES系统集成

摘要&#xff1a; 目前MES在制造行业变得炙手可热&#xff0c;然而很多企业都忽视了数据的源头&#xff0c;MES作为生产执行的信息化系统&#xff0c;我们该如何让其在企业中成功的实施&#xff0c;发挥更大的作用&#xff0c;这还需要PDM系统的支撑。本文就PDM与MES集成进行简…

css前端面试题(三)

文章目录 1、可继承属性和不可继承属性字体系列属性文本系列属性元素可见性列表布局属性光标属性 2、link和import的区别3、css优化4、 CSS预处理器/后处理器是什么&#xff1f;为什么要使用它们&#xff1f;5、单行、多行文本溢出隐藏6、实现一个扇形7、实现一个自适应的正方形…

【Axure高保真原型】人物卡片多条件搜索案例

今天和大家分享人物卡片多条件搜索的原型模板&#xff0c;我们可以输入姓名或者选择部门、岗位来快速筛选出对应的人物信息卡片。那这个模板是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;只需要在中继器表格导入图片和填写对应内容&#xff0c;即可自动生成交互效…

1600*A. LCM Challenge(数论 || 找规律)

解析&#xff1a; n<3&#xff0c;特判 n为奇数&#xff0c;则n、n-1、n-2必定互质&#xff0c;所以结果即为三者之和。 n为偶数&#xff0c; 不会严格证明原因&#xff0c;但是找找规律&#xff0c;是这样的...... #include<bits/stdc.h> using namespace std; #de…

ros----发布者和订阅者模型

话题模型&#xff1a; 如何自定义话题消息 1.定义msg文件 2.在package.xml中添加功能包依赖 <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>3.在CMakeList.txt文件中添加编译选项 4.编译生成语言的相…

网络工程师是干什么的?常见岗位有哪些?

网络工程师是做什么工作&#xff1f; 网络工程师能够从事计算机信息系统的设计、建设、运行和维护工作。一般来说&#xff0c;分硬件网络工程师和软件网络工程师两大类&#xff0c;硬件网络工程师以负责网络硬件等物理设备的维护和通信&#xff1b;软件网络工程师负责系统软件…

LeetCode(力扣)509. 斐波那契数Python

LeetCode509. 斐波那契数 题目链接代码 题目链接 https://leetcode.cn/problems/fibonacci-number/ 代码 class Solution:def fib(self, n: int) -> int:if n 0:return 0dp [0] * (n 1)dp[0] 0dp[1] 1for i in range(2, n 1):dp[i] dp[i - 1] dp[i - 2]return d…

IM即时通讯系统[SpringBoot+Netty]——梳理(总)

文章目录 一、为什么要自研一套即时通讯系统1、实现一个即时通讯系统有哪些方式1.1、使用开源产品做二次开发或直接使用1.2、使用付费的云服务商1.3、自研 2、如何自研一套即时通讯系统2.1、早期即时通讯系统是如何实现2.2、一套即时通讯系统的基本组成2.3、当下的即时通讯系统…

【milkv】st7735驱动

前言 本文介绍milkv-duo加载st7735的lcd屏幕&#xff0c;以及屏幕显示log。 参考文章&#xff1a; 记录为Linux配置spi屏幕&#xff08;st7735s&#xff09; https://community.milkv.io/t/milk-v-duo-spi-st7789/131 一、电路图 1.1 pin设置 打开spi2的引脚 duo-buildroot…

@Transactional失效场景/原因

文章目录 1.Transactional注解在非public方法上2.Transactional使用propagation设置错误&#xff08;有3种会失效&#xff09;3.Transactional使用rollbackFor设置错误4.A方法没有使用Transactional调用了B&#xff08;有被注解&#xff09;方法5.try catch了异常6.数据库引擎不…

AI绘制流程图

1、工具&#xff1a; 使用https://chat.openai.com/c/45a81a53-cced-43f7-be3e-e5e80f1e994fFlowchart Maker & Online Diagram Software 2、使用plantuml的过程&#xff1a; 复制代码&#xff0c;打开diagram.net&#xff0c;点击加号→高级→ plantUML&#xff0c;替换掉…

已解决 Go Error: cannot use str (type string) as type int in assignment

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

【openKylin】OpenKylin1.0 x86_64 VMWare安装手册

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1f338;文…

华为云ROMA Connect亮相Gartner®全球应用创新及商业解决方案峰会,助力企业应用集成和数字化转型

9月13日-9月14日 Gartner全球应用创新及商业解决方案峰会在伦敦举行 本届峰会以“重塑软件交付&#xff0c;驱动业务价值”为主题&#xff0c;全球1000多位业内专家交流最新的企业应用、软件工程、解决方案架构、集成与自动化、API等企业IT战略和新兴技术热门话题。 9月13日…

【结构体类型——详细讲解】

结构体 1.结构体类型声明 1.1结构体的概念 结构体是⼀些值的集合&#xff0c;这些值称为成员变量。结构体的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag { member-list; }variable-list;例如描述⼀个学⽣&#xff1a; struct Stu { char name[20]; //名字 i…

337.打家劫舍III

337. 打家劫舍 III - 力扣&#xff08;LeetCode&#xff09; 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个…

SpringSecurity 入门

文章目录 Spring Security概念快速入门案例环境准备Spring配置文件SpringMVC配置文件log4j配置文件web.xmlTomcat插件 整合SpringSecurity 认证操作自定义登录页面关闭CSRF拦截数据库认证加密认证状态记住我授权注解使用标签使用 Spring Security概念 Spring Security是Spring…

竞赛选题 基于深度学习的人脸性别年龄识别 - 图像识别 opencv

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…