linux——信号

news2025/1/12 1:38:47

✅<1>主页::我的代码爱吃辣
📃<2>知识讲解:Linux——进程等待
☂️<3>开发环境:Centos7
💬<4>前言:生活中处处有信号,linux中也有很多信号,OS使用来通知进程,控制进程,更好的管理进程。

目录

一.生活中的信号

二.技术应用角度的信号

三.信号的捕捉

 四.信号的产生

 1. 通过终端按键产生信号

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

3.由软件条件产生信号

 4. 硬件异常产生信号

 五.核心转储


一.生活中的信号

  1. 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”。
  2. 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  3. 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”。
  4. 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)。
  5. 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话。

二.技术应用角度的信号

  1. 用户输入命令,在Shell下启动一个前台进程。
  2. 用户按下 Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程。
  3. 前台进程因为收到信号,进而引起进程退出。

 测试代码:

#include <stdio.h>
#include <unistd.h>
int main()
{
    while (1)
    {
        printf("I am a process, I am waiting signal!\n");
        sleep(1);
    }
}

测试结果:

 注意:

  1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程能接到像,只有前台进程才 Ctrl-C 这种控制键产生的信号。
  3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

查看信号列表:

kill -l 

  1. Ctrl + C 就是给前台进程发送 2号信号 SIGINT。
  2. 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2。
  3. 编号34以上的是实时信号,本章只讨论编号34以下的信号,不讨论实时信号。
  4. 这些信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal。

三.信号的捕捉

当我们按下Ctrl + C 前台进程会收到 2 号信号。那我们如何验证进程收到了2号信号呢?

 介绍一个系统调用:

头文件: #include <signal.h>

typedef void (*sighandler_t)(int);

接口定义:sighandler_t signal(int signum, sighandler_t handler)。

作用:捕捉信号signnum,将signum的默认动作修改为 hander。

参数:1.signum信号编号   2.handler回调的方法。

测试代码:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <signal.h>

using namespace std;

void handle(int signum)
{
    cout << "I get a signal:" << signum << endl;
}

int main()
{

    signal(2, handle);

    while (1)
    {
        printf("I am a process, I am waiting signal!\n");
        sleep(1);
    }
}

测试结果:

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

在上述的代码中2号信号的默认动作是终止进程,当我们对2好信号进行捕捉,并且将2号信号的默认动作修改为我们自定义的一个函数。因此我们再次Ctrl + C,进程收到2号信号就不会终止,而是执行我们设计的函数。我们继续可以使用Ctrl + \ 终止进程。

 四.信号的产生

 1. 通过终端按键产生信号

 中断按键,就是我们的键盘,当我们按下Ctrl + C 时OS会向前台进程发送2号信号。还有Ctrl + \ 可以向前台进程发送。

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

#include <signal.h>

#include <sys/types.h>

int kill(pid_t pid, int signo);向指定进程发指定信号。
int raise(int signo);向调用进程发送指定信号。
这两个函数都是成功返回0,错误返回-1。

#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值,给进程发送6号信号,6号直接终止进程。

kill-测试代码:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

using namespace std;

void handle(int signum)
{
    cout << "I am child process,my PID :" << getpid() << ",I get a signal:" << signum << endl;
    // 进程退出
    exit(0);
}

int main()
{

    // 创建子进程
    pid_t pid = fork();
    if (pid == 0)
    {
        // 让子进程捕捉2号信号
        signal(2, handle);

        while (1)
        {
            printf("I am child process, my PID :%d,I am waiting signal!\n", getpid());
            sleep(1);
        }
    }

    int count = 5;
    while (count)
    {
        printf("%d秒之后我将给%d号进程发2号信号\n", count--, pid);
        sleep(1);
    }
    // 给子进程发送2号信号
    kill(pid, 2);
    sleep(1);
    return 0;
}

测试结果:

 raise-测试代码

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

using namespace std;

void handle(int signum)
{
    cout << "I am process,I get a signal:" << signum << endl;
    // 进程退出
    exit(0);
}

int main()
{

    // 让进程捕捉2号信号
    signal(2, handle);

    int count = 5;
    while (count)
    {
        printf("%d秒之后我将给自己发2号信号\n", count--);
        sleep(1);
    }
    // 给子进程发送2号信号
    raise(2);
    sleep(1);
    return 0;
}

测试结果:

abort-测试代码:

 代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

using namespace std;

void handle(int signum)
{
    cout << "I am process,I get a signal:" << signum << endl;
    // 进程退出
    exit(0);
}

int main()
{

    // 让进程捕捉6号信号
    signal(6, handle);

    int count = 5;
    while (count)
    {
        printf("%d秒之后我将给自己发6号信号\n", count--);
        sleep(1);
    }
    // 给子进程发送6号信号
    abort();
    sleep(1);
    return 0;
}

测试结果:

3.由软件条件产生信号

 SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。

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

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

 测试代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

using namespace std;
int main()
{

    // 设置闹钟
    alarm(5);
    int count = 5;
    while (count)
    {
        printf("%d秒之后闹钟响,进程终止\n", count--);
        sleep(1);
    }
    sleep(15);
    return 0;
}

测试结果:

 4. 硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。 

除0异常模拟:

测试代码:

int main()
{

    cout << "begin" << endl;
    cout << "begin" << endl;
    int num = 10;
    int tmp = num / 0;

    cout << "end" << endl;
    cout << "end" << endl;

    return 0;
}

 说明:在运行到除0的地方,进程直接终止了,原因是进程收到了8号信号。

野指针异常模拟:

测试代码:

int main()
{

    cout << "begin" << endl;
    cout << "begin" << endl;
    int *point = nullptr;
    *point = 100;

    cout << "end" << endl;
    cout << "end" << endl;

    return 0;
}

测试结果:

由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层面上,是被当成信号处理的。

 总结:

  • 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者。
  • 信号的处理是否是立即处理的?在合适的时候。
  • 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?进程PCB中。
  • 一个进程在没有收到信号的时候,能不能知道,自己应该对合法信号作何处理呢?知道。
     

 五.核心转储

我们在进程控制时,讲到进程等待,我们与遇到过这个概念:

首先解释什么是核心转储(Core Dump)。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c1024

  1. SIGINT信号Action是Term,代表直接退出不进行多余的动作
  2. SIGQUIT信号Action是Core,代表退出前进行核心转储。

 使用命令$:ulimit -a 可以查看进程的资源分配信息,其中也包括core 文件大小。

 使用命令:ulimit -c 10240,给core dump添加大小。

 测试代码:

int main()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        cout << "begin" << endl;
        cout << "begin" << endl;
        int *point = nullptr;
        *point = 100;

        cout << "end" << endl;
        cout << "end" << endl;
    }

    int status = 0;
    waitpid(pid, &status, 0);
    cout << "core dump:" << ((status >> 7) & 1) << endl;
    cout << "signal:" << (status & 0x3f) << endl;

    return 0;
}

 测试结果:

 说明:

  • 当我们放开核心转储以后,之后执行的程序一旦收到core信号,就会在当前目录下生成一个core.XXX核心转储文件。
  • 这个文件是一个二进制文件。
  • 可以使用gdb对debug可执行程序进行调试,然后使用core-file core.XXX查看核心转储信息。

查看核心转储文件:

  1. 使用debug编译文件。
  2. 使用gdb调试可执行程序。
  3. 在gdb中使用core-file产看core.XXX文件按

 

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

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

相关文章

算法强训:第三十四天

文章目录 收件人列表养兔子一、收件人列表OJ链接 本题思路:先接收到一个数字,代表接下来是多少组数据 ,逐个接收每个名字,如果名字中没有,或者 则直接输出,否则在改名字前后拼接"\""再输出,除最后一个名字外,每个名字之后都有一个", " ,该组用例…

(三)激光线扫描-中心线提取

光条纹中心提取算法是决定线结构光三维重建精度以及光条纹轮廓定位准确性的重要因素。 1. 光条的高斯分布 激光线条和打手电筒一样,中间最亮,越像周围延申,光强越弱,这个规则符合高斯分布,如下图。 2. 传统光条纹中心提取算法 传统的光条纹中心提取算法有 灰度重心法、…

pycharm中个人编程时常用到的快捷键

pycharm中个人编程时常用到的快捷键&#xff1a; 仅个人经验总结&#xff0c;不为其他&#xff01; 1.CTRLShiftAlt鼠标选择多个位置 可以同时在多个位置进行编辑同样的内容 2. Ctrel Alt L快速将代码格式标准化 3. Ctrl F 在当前py文件中查找 4. Ctrl R快速替换当前…

哈哈,我保研985了,之后会出一期保研经验分享

哈哈&#xff0c;我保研了&#xff0c;之后会出一期保研经验分享 个人背景 学校&#xff1a;河南某四非&#xff0c;计算机科学与技术专业英语成绩&#xff1a;四级439&#xff0c;六级438&#xff08;夏令营无六级&#xff09;科研经历&#xff1a;一个软著、国家级大创&…

如何在idea中隐藏文件或文件夹

例如我想要隐藏如下文件 只需要点击file->settings editor->file types->ignores Files and Folders-> 然后按照图片点击顺序操作即可 添加完毕点击apply->ok 隐藏成功后效果如下&#xff1a;

基于蚁狮优化的BP神经网络(分类应用) - 附代码

基于蚁狮优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于蚁狮优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.蚁狮优化BP神经网络3.1 BP神经网络参数设置3.2 蚁狮算法应用 4.测试结果&#xff1a;5.M…

EasyX图形库note4,动画及键盘交互

大家好&#xff0c;这里是Dark Flame Master&#xff0c;专栏从这篇开始就会变得很有意思&#xff0c;我们可以利用今天所学的只是实现很多功能&#xff0c;同样为之后的更加好玩的内容打下基础&#xff0c;从这届开始将会利用所学的知识制作一些小游戏&#xff0c;废话不多说&…

计算机毕业设计 基于SSM的在线预约导游系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

UCOS的任务创建和删除

一、任务创建和删除的API函数 1、任务创建和删除本质就是调用uC/OS的函数 API函数 描述 OSTaskCreate() 创建任务 OSTaskDel() 删除任务 注意&#xff1a; 1&#xff0c;使用OSTaskCreate() 创建任务&#xff0c;任务的任务控制块以及任务栈空间所需的内存&#xff0c…

基于SpringBoot的流浪动物管理系

基于SpringBoot的流浪动物管理系的设计与实现&#xff0c;前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 首页 后台登陆界面 管理员界面 摘要 基于Spring Boot的…

常见的几种排序方式

常见的几种排序方式 1. 排序的概念2. 常见排序算法的实现2.1 插入排序2.1.1基本思想2.1.2 直接插入排序2.1.3 希尔排序( 缩小增量排序 ) 2.2 选择排序2.2.1基本思想2.2.2 直接选择排序:2.2.3 堆排序 2.3 交换排序2.3.1冒泡排序2.3.2 快速排序 2.4 归并排序2.4.1 基本思想2.4.2 …

力扣-383.赎金信

Idea 使用一个hashmap 或者一个int数组存储第二次字符串中每一个字符及其出现的次数 遍历第一个字符串&#xff0c;讲出现的重复字符减1&#xff0c;若该字符次数已经为0&#xff0c;则返回false AC Code class Solution { public:bool canConstruct(string ransomNote, strin…

最短路径专题3 最短距离-多边权

题目&#xff1a; 样例&#xff1a; 输入 4 5 0 2 0 1 2 1 0 2 5 1 0 3 1 2 1 2 1 6 3 2 2 3 输出 3 5 思路&#xff1a; 根据题目意思&#xff0c;其实还是Dijkstra 的题目&#xff0c;不同的是&#xff0c;多了一个最少花费边权的这个点&#xff0c;多添加一个spend数组&am…

【Maven基础篇-黑马程序员】Maven项目管理从基础到高级,一次搞定!

文章目录 前言Maven简介Maven是什么Maven的作用 Maven的下载与安装Maven基础概念仓库坐标仓库配置全局setting与用户setting区别 第一个Maven程序&#xff08;手工制作&#xff09;第一个Maven程序&#xff08;IDEA生成&#xff09;使用模版&#xff08;骨架&#xff09;创建Ma…

(C++版)ROS2 bind函数解读

在ros2的发布者节点里面有这么一句话&#xff1a;估计没有学过C的人不太理解&#xff0c;这里我就发发好心帮忙解读一下timer_ this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this)); timer_ this->create_wall_timer(500ms, std…

Java - 基本数据类型和封装类型

基本类型有默认值&#xff0c;而包装类型初始为null。然后再根据这两个特性进行分业务使用&#xff0c;在阿里巴巴的规范里所有的POJO类必须使用包装类型&#xff0c;而在本地变量推荐使用基本类型。 Java语言提供了八种基本类型。六种数字类型&#xff08;四个整数型&#xff…

BP神经网络的MATLAB实现(含源代码)

BP(back propagation)神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念&#xff0c;是一种按照误差逆向传播算法训练的多层前馈神经网络&#xff0c;是应用最广泛的神经网络模型之一 具体数学推导以及原理在本文不做详细介绍&#xff0c;本文将使用MATLAB进行B…

ASUS (k013) ME176CX不进入系统恢复出厂设置的方法

k013 me176cx ASUS k013 ME176CX不进入系统恢复出厂设置的方法 当忘记系统密码或系统异常导致无法进入系统时&#xff0c;可以按以下步骤尝试不进入系统恢复出厂设置来解决。 注意&#xff1a;执行恢复出厂设置前&#xff0c;请先将资料备份至外接设备&#xff0c;否则资料都…

十四天学会C++之第三天(数组和字符串)

1. 数组的定义和初始化 数组是一种由相同数据类型的元素组成的集合&#xff0c;这些元素按照一定的顺序存储在连续的内存位置上。数组的大小在创建时是固定的&#xff0c;无法在运行时改变。 在C中&#xff0c;数组的定义和声明非常简单。定义一个数组&#xff1a; 数据类型…

基于被囊群优化的BP神经网络(分类应用) - 附代码

基于被囊群优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于被囊群优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.被囊群优化BP神经网络3.1 BP神经网络参数设置3.2 被囊群算法应用 4.测试结果&#x…