Linux知识点 -- 进程信号(一)

Linux知识点 – 进程信号(一)

文章目录

  • Linux知识点 -- 进程信号(一)
  • 一、理解信号
    • 1.理解Linux信号
    • 2.信号的产生与处理
    • 3.常见的信号
    • 4.如何理解组合键变成信号
    • 5.如何理解信号被进程保存
  • 二、信号的产生
    • 1.键盘产生
    • 2.核心转储
    • 3.系统调用接口产生信号
    • 4.由软件条件产生信号
    • 5.硬件异常产生信号


一、理解信号

1.理解Linux信号

  • Linux信号:
    本质是一种通知机制,用户or操作系统通过发送一定的信号,通知进程,某些事件已经发生,你可以在后续进行处理;

  • 信号结论:
    (1)进程要处理信号,必须要具备识别的能力(看到 + 处理动作);
    (2)进程通过程序员编写的代码逻辑,能够识别信号;
    (3)信号的产生是随机的,进程可能在忙自己的事情,所以,信号的后续处理,可能不是立即处理的;
    (4)进程会临时记录下对应的信号,方便后续进行处理;
    (5)进程会再合适的时候处理信号;
    (6)一般而言,信号的产生相对于进程而言是异步的;

2.信号的产生与处理

  • 信号的产生:
    当一个进程正在运行时,我们通过键盘上的ctrl + c可以杀掉进程,这本质就是通过键盘的组合键向目标进程发送2号信号;

  • 信号处理常见的方式:
    (1)默认(进程自带的,程序员写好的逻辑)
    (2)忽略
    (3)自定义动作(捕捉信号)

3.常见的信号

在Linux下输入kill -l指令,可以查看系统的信号:
在这里插入图片描述
其中,前31个是常用的信号;

4.如何理解组合键变成信号

键盘的工作方式是通过中断方式进行的,当然能够识别组合键;

  • 操作系统处理信号的流程
    OS解释组合键 -> 查找进程列表 -> 前台运行的进程 -> OS写人对应的信号到进程内部的位图结构中

5.如何理解信号被进程保存

信号的数据主要有两类:信号种类以及是否产生;

在进程PCB中的task_struct中有保存信号的相关数据结构 – 位图(unsigned int),每一位都表示不同信号,0和1代表有没有接收该信号;

信号发送的本质:OS向目标进程写信号,OS直接修改目标进程PCB中的指定的位图的结构,完成发送信号的过程;

二、信号的产生

1.键盘产生

我们知道信号可以从键盘产生,当我们产生信号后,信号的处理方式有三种,接下来我们使用自定义捕捉来处理键盘产生的信号;

  • signal函数:
    在这里插入图片描述
    参数:
    sighandler:函数指针,作为回调函数的参数使用;
    signum:信号名称;
    handler:处理信号的函数的指针;
    这个函数的功能是:我们一旦收到了指定的信号,signal函数会将信号的编号传回给回调函数的参数;
#include<iostream>
#include<signal.h>
#include<unistd.h>

using namespace std;

void catchSig(int signum)
{
    cout << "进程捕捉到了一个信号,正在处理中:" << signum << "pid: " << getpid() << endl;
}


int main()
{
    //signal(2, catchSig);//signal第一个参数可以传信号名,也可以传对应的序号
    signal(SIGINT, catchSig);

    while(true)
    {
        cout << "我是一个进程,我正在运行,pid: " << getpid() << endl;
        sleep(1);
    }


    return 0;
}

上面的代码在捕捉2号信号,当捕捉到后,会调用自己的处理函数catchSig;
运行结果:
在这里插入图片描述
(1)进程不断在运行,当我们按下键盘组合键ctrl + c时,触发2号信号,进程就会执行我们自定义捕捉的处理方式,但是进程并没有结束;
(2)这是因为特定信号的处理方式,一般只有一个,我们将2号信号的处理方式改为自定义的处理方式,系统默认的处理方式就失效了;
(3)signal函数仅仅是修改了进程对特定信号的后续处理动作,不是直接调用对应的处理动作;只有当对应信号触发的时候,signal函数会回调catchSig函数,对信号进行处理;
(4)如果后续没有任何SIGINT信号产生,那么catchSig永远不会被调用;

2.核心转储

在上面的代码增加对3号信号的捕捉(3号信号快捷键是ctrl + \ ,也是让进程退出):

int main()
{
    //signal(2, catchSig);//signal第一个参数可以传信号名,也可以传对应的序号
    signal(SIGINT, catchSig);
    signal(SIGQUIT, catchSig);//3号信号

    while(true)
    {
        cout << "我是一个进程,我正在运行,pid: " << getpid() << endl;
        sleep(1);
    }


    return 0;
}

在这里插入图片描述
man 7 signal指令打开手册,查看信号的处理动作:
在这里插入图片描述
发现2号指令后面的动作是term,而3号的是core,这是进程的核心转储功能
我们在学习进程等待时,子进程的status中,被信号所杀的子进程的status会有一个core dump位:
在这里插入图片描述
这个位就是标志该进程是否发生了核心转储;

  • 核心转储:
    当进程出现某种异常的时候,是否由OS将当前进程在内存中的相关核心数据,转存到磁盘中;
    核心转储主要为了调试,这是它的主要应用场景;
    一般而言,云服务器(生产环境)的核心转储功能都是关闭的;

  • 打开核心转储:
    在这里插入图片描述
    ulimit -a:查看,可以看到core file size是0;
    ulimit -c 大小:设置core file的大小;

    再次
    再次查看可以发现,core file size变为了10240;
    这仅仅是在自己的终端中打开了核心转储,如果重启云服务器,又会恢复回去;

只要是信号动作是core的,都会触发核心转储,在进程运行时触发核心转储,会报错core dumped,并产生一个core文件,后缀是进程的pid;
触发3号信号终止进程,并产生相应的core文件:
在这里插入图片描述
8号信号也是core信号,在进程中触发除0错误就会触发8号信号:
在这里插入图片描述

int main()
{
    while(true)
    {
        sleep(1);
        int a = 10;
        a /= 0;
        cout << "我是一个进程,我正在运行,pid: " << getpid() << endl;
        sleep(1);
    }

    return 0;
}

在这里插入图片描述
core文件是能够用来调试的,用gdb加载core文件:
在这里插入图片描述
core文件会告诉我们在哪里发生了错误,触发的是什么信号;

3.系统调用接口产生信号

  • kill函数:是向进程发送信号的系统调用接口
    在这里插入图片描述
    功能:向pid进程发送sig信号;
    能够发送系统信号,与kill指令的功能一致;

模拟kill指令代码:

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

using namespace std;

static void Usage(string proc)
{
    cout << "Usage:\r\n\t" << proc << " signumber processid" << endl;
}

// ./system_signal 2 pid
int main(int argc, char* argv[])
{
    if(argc != 3)//不满足格式要求
    {
        Usage(argv[0]);
        exit(1);
    }

    int signumber = atoi(argv[1]);
    int procid = atoi(argv[2]);

    kill(procid, signumber);

    return 0;
}

运行结果:
将sleep 10000这个进程,通过我们自己模拟的kill指令,向进程发送信号,终止该进程;
在这里插入图片描述

  • raise函数:给自己发送sig信号
    在这里插入图片描述
int main(int argc, char* argv[])
{

    cout << "运行中" << endl;
    sleep(1);
    raise(8);

    return 0;
}

运行结果:
进程向自己发送了8号信号;
在这里插入图片描述

  • abort函数:给自己发送和abort信号,就是自己终止自己
    在这里插入图片描述
    在这里插入图片描述

  • 系统调用接口的流程:
    用户调用系统接口 -> 执行OS对应的系统调用代码 -> OS提取参数或者设置特定的数值 -> OS向目标进程写信号 -> 修改对应的信号标记位 -> 进程会处理信号 -> 执行对应的处理动作

4.由软件条件产生信号

  • alarm:设定闹钟
    在这里插入图片描述

代码验证1s内CPU能执行多少次++:

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

using namespace std;

int count = 0;

void catchSig(int signum)
{
    cout << "count: " << count << endl;
}

int main(int argc, char* argv[])
{
    //验证1s之内,CPU能进行多少次++
    alarm(1);
    signal(SIGALRM, catchSig);

    while(true)
    {
        count ++;
    }

    return 0;
}

运行结果:
在这里插入图片描述
这段代码是使用了alarm作为时钟,当这个函数运行之后,就会定时一段时间,当这段时间到了之后,就会向进程发送信号,这就是软件发送信号;
当我们设定一个闹钟之后,一旦触发闹钟,就自动移除了,如果想要定期做一件事情,可以在捕捉完信号后,再设一个闹钟;

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

using namespace std;

int count = 0;

void catchSig(int signum)
{
    cout << "count: " << count << endl;
    alarm(1);//捕捉完闹钟信号后,再设一个闹钟
}

int main(int argc, char* argv[])
{
    //验证1s之内,CPU能进行多少次++
    alarm(1);
    signal(SIGALRM, catchSig);

    while(true)
    {
        count ++;
    }

    return 0;
}

运行结果:
在这里插入图片描述

设置一个定时器

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<functional>
#include<vector>
#include<sys/types.h>
#include<sys/wait.h>

using namespace std;

typedef function<void ()> func;
vector<func> callbacks;
int count = 0;

void showLog()
{
    cout  << "日志功能" << endl;
}

void logUser()
{
    if(fork() == 0)
    {
        execl("/usr/bin/who", "who", nullptr);
        exit(1);
    }
    wait(nullptr);
}

void showCount()
{
    cout << "count : " << count << endl;
}

void catchSig(int signum)
{
    for(auto &f : callbacks)
    {
        f();
    }
    alarm(1);
}

int main(int argc, char* argv[])
{
    signal(SIGALRM, catchSig);
    alarm(1);

    callbacks.push_back(showCount);
    callbacks.push_back(showLog);
    callbacks.push_back(logUser);
    

    while(true)
    {
        count ++;
    }

    return 0;
}

运行结果:
在这里插入图片描述

5.硬件异常产生信号

捕捉8号信号:

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

using namespace std;

void catchSig(int signum)
{
    cout << "进程捕捉到了一个信号:" << signum << endl;
    sleep(1);
}

int main()
{
    signal(SIGFPE, catchSig);

    int a = 10;
    a /= 0;
    while (true)
    {
        sleep(1);
    }

    return 0;
}

运行结果:
在这里插入图片描述

  • 系统一直在捕捉8号信号,这是因为8号信号即除0信号是硬件触发的;
    (1)进行计算的CPU是硬件;
    (2)CPU内部是有寄存器的,状态寄存器(位图)有对应的状态标记位,溢出标记位,O3S会自动进行计算完毕之后的检测,如果溢出标记位是1,OS会识别到有溢出问题,立即找到当前谁在运行,并提取pid,OS完成发送信号的过程,进程会在合适的时候,进行处理;
    (3)一旦出现硬件异常,进程不一定会退出;
    (4)一直捕捉到8号信号的原因是:寄存器标志位异常一直没有被解决;

  • 遇到野指针问题,称之为段错误,11号信号就是野指针;
    在这里插入图片描述
    在这里插入图片描述
    野指针越界是硬件信号的原因:
    (1)进程必须通过地址,找到目标位置;
    (2)语言上面的地址,全都是虚拟地址;
    (3)将虚拟地址转换为物理地址 ,需要页表 + MMU(Memory Manager Unit,硬件);
    (4)野指针越界,即访问非法地址,MMU转化的时候,一定会报错;

所有的信号,无论来源,最终都是被OS识别,解释并发送的;

  • 问题
    在这里插入图片描述

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

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

相关文章

go-zero 是如何实现计数器限流的?

原文链接&#xff1a; 如何实现计数器限流&#xff1f; 上一篇文章 go-zero 是如何做路由管理的&#xff1f; 介绍了路由管理&#xff0c;这篇文章来说说限流&#xff0c;主要介绍计数器限流算法&#xff0c;具体的代码实现&#xff0c;我们还是来分析微服务框架 go-zero 的源…

LinearAlgebraMIT_7_Ax=0

上节课讲了向量子空间中的列空间和零空间&#xff0c;这节课来讲零空间的(Special solutions)特解&#xff0c;也就是Ax0的特解。在求解特解的核心便是使用消元法求得(row echelon form)阶梯矩阵或者(reduced row echelon form/RREF)最简矩阵。 我们接下来举一个例子&#xff…

【sonar】安装sonarQube免费社区版9.9【Linux】【docker】

文章目录 ⛺sonarQube 镜像容器⛺Linux 安装镜像&#x1f341;出现 Permission denied的异常&#x1f341;安装sonarQube 中文包&#x1f341;重启服务 ⛺代码上传到sonarQube扫描&#x1f341;java语言配置&#x1f341;配置 JS TS Php Go Python⛏️出现异常sonar-scanner.ba…

【设计模式】观察者模式

什么是观察者模式&#xff1f; 观察者模式&#xff08;又被称为发布-订阅&#xff08;Publish/Subscribe&#xff09;模式&#xff0c;属于行为型模式的一种&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态…

现代C++中的从头开始深度学习:【5/8】卷积

一、说明 在上一个故事中&#xff0c;我们介绍了机器学习的一些最相关的编码方面&#xff0c;例如 functional 规划、矢量化和线性代数规划。 现在&#xff0c;让我们通过使用 2D 卷积实现实际编码深度学习模型来开始我们的道路。让我们开始吧。 二、关于本系列 我们将学习如何…

VS+Qt+C++旅游景区地图导航源码实例

程序示例精选 VSQtC旅游景区地图导航 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<VSQtC旅游景区地图导航>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。…

服务器数据恢复-RAID5上层Hyper-V虚拟机数据恢复案例

服务器数据恢复环境&#xff1a; 一台Windows Server服务器&#xff0c;部署Hyper-V虚拟化环境&#xff0c;虚拟机的硬盘文件和配置文件存放在一台DELL存储中。该存储中有一组由4块硬盘组建的RAID5阵列&#xff0c;用来存放虚拟机的数据文件&#xff0c;另外还有一块大容量硬盘…

Android Studio实现Spinner下拉列表

效果图 点击下拉列表 点击某一个下拉列表 MainActivity package com.example.spinneradapterpro;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.Spinn…

原型模式与享元模式:提升系统性能的利器

原型模式和享元模式&#xff0c;前者是在创建多个实例时&#xff0c;对创建过程的性能进行调优&#xff1b;后者是用减 少创建实例的方式&#xff0c;来调优系统性能。这么看&#xff0c;你会不会觉得两个模式有点相互矛盾呢&#xff1f; 在有些场景下&#xff0c;我们需要重复…

Java # Spring(2)

一、Spring事物 一、分类 编程式事物&#xff1a;代码中硬编码&#xff08;不推荐使用&#xff09; 声明式事物&#xff1a;配置文件中配置&#xff08;推荐使用&#xff09; 分类&#xff1a; 基于xml的声明式事物基于注解的声明式事物 二、隔离级别 ISOLATION_DEFAULT&…

Android 视频播放器dkplayer

列表播放如图所示&#xff1a; 一、依赖 //添加RecyclerView的依赖包implementation androidx.recyclerview:recyclerview:1.2.1// 异步加载图片依赖implementation com.squareup.picasso:picasso:2.5.2// 上拉刷新、下来加载依赖implementation com.scwang.smartrefresh:Smart…

mysql之存储过程

目录 一、mysql之存储过程的相关知识 1&#xff09;存储过程的概念 2&#xff09;存储过程的优点 二、存储过程的管理 1&#xff09;创建存储过程 基本格式&#xff1a; 2&#xff09;调用存储过程 格式&#xff1a; call 存储过程名称 3&#xff09;查看存储过程 查…

Leetcode-每日一题【剑指 Offer 13. 机器人的运动范围】

题目 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&#xff0c;也不能进入行坐标和列坐标的数位之和大于k的格子。例…

51单片机学习--红外遥控(外部中断)

需要利用下面这个红外接收头&#xff0c;OUT口会发出红外信号对应的高低电平&#xff0c;由于发送的速度很快&#xff0c;所以需要把OUT引脚接在外部中断引脚上&#xff0c;当OUT一旦产生下降沿&#xff0c;马上进中断&#xff0c;这样响应会更及时。 外部中断引脚位于P3_2和P…

【云原生】kubernetes控制器deployment的使用

目录 ​编辑 1 Controller 控制器 1.1 什么是 Controller 1.2 常见的 Controller 控制器 1.3 Controller 如何管理 Pod 2 Deployment 2.1 创建 deployment 2.2 查看 deployment 2.3 扩缩 deployment 2.4 回滚 deployment 2.5 删除 deployment 1 Controller 控制器 …

条条大路通罗马系列—— 使用 Hiredis-cluster 连接 Amazon ElastiCache for Redis 集群

前言 Amazon ElastiCache for Redis 是速度超快的内存数据存储&#xff0c;能够提供亚毫秒级延迟来支持 实时应用程序。适用于 Redis 的 ElastiCache 基于开源 Redis 构建&#xff0c;可与 Redis API 兼容&#xff0c;能够与 Redis 客户端配合工作&#xff0c;并使用开放的 Re…

【算法篇C++实现】五大常规算法

文章目录 &#x1f680;一、分治法⛳&#xff08;一&#xff09;算法思想⛳&#xff08;二&#xff09;相关代码 &#x1f680;二、动态规划算法⛳&#xff08;一&#xff09;算法思想⛳&#xff08;二&#xff09;相关代码 &#x1f680;三、回溯算法⛳&#xff08;一&#xf…

数据挖掘具体步骤

数据挖掘具体步骤 1、理解业务与数据 2、准备数据 数据清洗&#xff1a; 缺失值处理&#xff1a; 异常值: 数据标准化&#xff1a; 特征选择&#xff1a; 数据采样处理&#xff1a; 3、数据建模 分类问题&#xff1a; 聚类问题&#xff1a; 回归问题 关联分析 集成学习 image B…

区块链学习6-长安链部署:如何创建特定共识节点数和同步节点数的链

正常prepare的时候只支持4 7 13 16个节点个数&#xff0c;想要创建10个节点&#xff0c;其中5个是共识节点&#xff0c;如何实现&#xff1f; 1. 注释掉prepare.sh的这几行&#xff1a; 2. 修改 crytogen的模板文件&#xff1a; 如果是cert模式&#xff1a;chainmaker-crypt…

idea+gradle阅读spring5.2.9源码之源码构建报错解决方案

注意 1、先确保gradle版本和spring、jdk版本对应 本文:gradle:5.6.4/spring 5.2.9/jdk1.8&#xff08;gradle和jdk都要先安装好&#xff0c;gradle还要配置好本地资源文件路径&#xff09; 2、原来项目乱了的话&#xff0c;先重新导入下载的源码项目 3、进入源码所在根目录&…