【1++的Linux】之信号(一)

news2025/1/11 11:31:27

👍作者主页:进击的1++
🤩 专栏链接:【1++的Linux】

文章目录

  • 一,关于信号
  • 二,深剖信号的产生
    • 1. 键盘组合建产生信号
    • 2.核心转储
    • 3. 系统调用接口产生信号
    • 4. 由软件条件产生信号
    • 5. 硬件异常产生信号

一,关于信号

1. 什么是信号

在我们的生活中存在许多的信号:红绿灯,闹钟等都是信号,这些信号的本质都是告诉我们对应的信号发送后,我们要有相应的动作应对。
Linux信号的本质也是如此:是一种通知机制,用户或者OS发送给进程信号,通知进程某件事已经发生,你要进行后续的处理

2. 信号和进程

就像红绿灯一样,我们要能够处理信号,必须具备能识别信号的能力。那么进程也是一样的,在处理信号前要能够识别相应信号是什么意思。
进程怎么能够识别信号呢?------通过程序员。
信号的产生是随机的,进程可能在忙 自己的事情,暂时没办法处理信号,那么就会在后续才进行处理,并不是要立刻进行处理。并且进程会临时记录下信号,方便后续的处理。
信号的产生一般而言对于进程是异步的。

3. 信号是怎么产生的

我们使用xshell时,经常会利用键盘组合键 ctrl+c进行终止程序的操作。
这本质就是向相应的进程发送信号。或者我们可以通过命令直接向对应的进程发送信号也是可以的。

如图就是我们Linux中常用的信号

其中1-31时我们的普通信号 34-64是我们的实时信号。

在这里插入图片描述

对于信号的处理我们有三种处理方式:

默认(进程自带的,已经写好的逻辑 )
忽略
自定义动作(捕捉信号)

组合键是如何变为信号的呢?

键盘的工作原理是:中断。
组合键在程序中已经有相应的解释。因此OS能够对其做出解释,接着OS会查找进程列表,找到前台运行的进程,最后将对应的信号写入该进程的位图结构中,等待该进程去处理。

4. 管理信号

信号是作用于进程的,OS又作为进程的管理者,因此信号最终都是要通过进程发送给进程的。而信号又不止一个,是不是要对信号进行管理?怎么管理?先描述,后组织。因此在每个进程的PCB中都必须要有保存信号的相关数据结构。Linux中有62种信号,我们在使用信号时,在乎的是信号有无。因此我们可以用位图来保存信号。

因此我们也就知道了发送信号的本质是:在OS直接将目标进程PCB中的信号位图中指定位置置为1 。

二,深剖信号的产生

1. 键盘组合建产生信号

我们前面说过ctrl +c 可以终止一个进程,并且其发送的是二号信号,那么该如何证明呢?
在这里插入图片描述
我们上面说过,信号 处理有三种方式,该函数可以修改我们信号对应的默认处理方式。

第一个参数:是int类型,刚刚查看的信号我们除了可以看见信号名称外还能看到编号,在系统中,信号编号就是整数,每一个信号都是被#define定义出来的,我们既可以使用信号名又可以使用数字。这里的参数就是信号编号。

第二个参数:是返回值为void,参数为int的函数指针

signal的返回值是指向之前的信号处理程序的指针。

下面我们来看代码:

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

void func(int signnum)
{
    std::cout<<"我是 "<<getpid()<<"我正在处理信号: "<<signnum<<std::endl;
}
int main()
{
    signal(2,func);
    while(true)
    {
        std::cout<<"hellow world"<<std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
当我们按下组合键ctrl c 时我们发现其对应的确实是2号信号。

ctrl + \ 也可以终止进程
Ctrl + \ 对应3号
Ctrl + z (暂停)对应20号。

下面我们再来谈谈singal这个函数。
在这里插入图片描述
当我们调用这个函数时,系统并不会立刻执行func这个回调函数,只有接收到信号的时候才会调用,也就是说singal只修改了对信号的处理方法。

若我们通过./mytest + &使得进程在后台运行
在这里插入图片描述

此时我们发现通过组合键就无法发送信号了
此时我们通过命令来直接杀死该进程
在这里插入图片描述
键盘产生的信号,只能用来终止前台进程,也就是说只能用来终止那些阻塞你执行命令的程序。

ps: 9号信号不可以被捕捉(自定义)!

2.核心转储

我们通过man 7 signal就可以查看信号的默认行为

在这里插入图片描述
我们在前面说过:status我们只研究其低十六位:次低八位表示退出状态;最低七位表示退出信号,中间那一位叫做core-dump我们现在只需知道它是用于调试的。
core dump就是我们所说的核心转储.
一般而言云服务器,其默认核心转储功能是关闭的。

core dump标志位代表了是否发生了核心转储
我们来看下面这段代码:

int main()
{
    pid_t pid=fork();
    while(pid==0)
    {
        sleep(1);
        std::cout<<"我是子进程:"<<getpid()<<std::endl;
        int a=10;
        a/=0;
    }

    int status=0;
    waitpid(-1,&status,0);
    std::cout<<"退出信号: "<<(status & 0x7f)<<"是否发生了核心转储:"<<((status>7)&1)<<std::endl;
}

在这里插入图片描述
我们可以看到其标志位显示其发生了核心转储,并且生成了core文件。那么这个文件有什么用呢?
我们可以用waitpid()中的status的次低7位获取到进程退出的信号,知道信号我们就知道了崩溃的原因。除此之外,我们还想知道进程是在哪一行崩溃的。因此通过核心转储生成的该文件中就会有记录。

我们可以使用ulimit -a 进行查看core dump。
通过ulimit -c 来进行设置。

在linux中,当一个进程退出的时候,它的退出码和退出信号都会被设置(正常情况)。

当一个进程异常的时候,进程的退出信号会被设置,表明当前进程退出的原因。

如果必要,OS会设置退出信息中的core dump标志位,并将进程在内存中的数据转储到磁盘当中,方便我们后期调试。

看下面这段代码:

int main()
{
  int a=0;
  a/=0;
  return 0;
}

在这里插入图片描述
我们的云服务器中的core dump默认是关掉的
在这里插入图片描述
打开后其也确实生成了core文件。
在这里插入图片描述
我们加-g选项后生成可执行程序。gbd ./test 进行调式,再输入core-file core.pid 就可以看到奔溃原因和第几行奔溃的了。

我们先让程序出异常,然后在用gdb调试,直接用core-file 命令得到了错误的原因和错误的行数。这种方案我们称为事后调试。

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

在这里插入图片描述
我们可以使用系统调用接口kill来发送命令

参数pid: 进程pid
sig: 发送几号信号
返回值:成功返回0,失败返回-1

下面我们通过系统调用接口来模拟实现一个kill命令。

#include<iostream>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
using namespace std;
int main(int argv,char* argc[])
{
     if(argv!=3)
    {
        cout<<"argv!=3 "<<"proc "<<"sign "<<"who"<<endl;
    }
    
    int sign=atoi(argc[1]);
    int who=atoi(argc[2]);
    kill(who,sign);
    cout<<sign<<" "<<who<<endl;
    return 0;
}

我们用自己模拟实现的kill去杀掉sleep进程。
在这里插入图片描述

除了上述这样的系统调用,我们还有raise , abort等这样的系统调用接口。

raise:自己给自己发信号
在这里插入图片描述

#include<iostream>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
using namespace std;
int main()
{
   int count=5;
   while(count)
   {
    cout<<"等待:"<<count--<<endl;
    sleep(1);
   }

    cout<<"发送信号"<<endl;
    raise(8);
}

在这里插入图片描述

abort:给自己发送六号信号,若未捕获,则终止当前进程
在这里插入图片描述

int main()
{
   int count=5;
   while(count)
   {
    cout<<"等待:"<<count--<<endl;
    sleep(1);
   }

    cout<<"发送信号"<<endl;
    abort();
}

在这里插入图片描述

如何理解系统调用接口发送信号:用户调用系统接口—》执行OS对应的系统调用代码—》OS提取参数,或者设置特定值—》OS向对应进程写信号—》修改对应进程的信号标记为—》等待进程后续处理----》处理成功。

4. 由软件条件产生信号

我们在前面提到过匿名管道,当读写的任意一端关闭后,都会造成对立的进程退出,下面我们进行测试。

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<cstring>
#include<sys/wait.h>
#include<sys/types.h>
using namespace std;
int main()
{
    int pipefd[2];
    int n=pipe(pipefd);
    assert(n!=-1);
    pid_t pid=fork();
        if(pid==0)
        {
            close(pipefd[0]);
            const char* buffer="hellow world";
           while(true)
           {
             int s=write(pipefd[1],buffer,strlen(buffer));
             assert(s!=0);
             sleep(1);
           }
           exit(1);
        }

        char buffer[1024];
        memset(buffer,'\0',1024);
        close(pipefd[1]);
        //close(pipefd[1]);
        while(true)
        {
            int n=read(pipefd[0],buffer,sizeof(buffer)-1);
            if(n>0)
            {
                printf("%s\n",buffer);
            }
            else
            {
                cout<<"退出"<<endl;
                break;

            }

            sleep(1);
            close(pipefd[0]);
        }

        int status;
        waitpid(-1,&status,0);
        close(pipefd[0]);
        cout<<"退出信号"<<(status&(0x7f))<<" "<<"是否核心转储"<<((status>>7)&1)<<endl;

    return 0;
}

在这里插入图片描述

我们发现当关闭父进程中的读端时,OS会向子进程发送13号信号(SIGPIPE),终止子进程。

在这里插入图片描述

设置一个计时器,会延迟的向我们发送一个信号。比如second设置成10,就代表着过10秒后给我们发送一个sigalrm信号也就是14号信号。

这个函数的返回值是 0 或者是以前设定的闹钟时间还余下的秒数。打个比方 , 某人要小睡一觉 , 设定闹钟为 30 分钟之后响,20 分钟后被人吵醒了 , 还想多睡一会儿 , 于是重新设定闹钟为 15 分钟之后响 ,“ 以前设定的闹钟时间还余下的时间 ” 就是10 分钟。如果 seconds 值为 0, 表示取消以前设定的闹钟 , 函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

void handler(int signum)
{
    cout<<"闹钟响了"<<endl;

}
int main()
{
    signal(14,handler);
    alarm(5);
    while(true)
    {
        cout<<"I am process"<<endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
若我们取消进程对alarm信号的自定义处理方式,那么其默认处理方式为:
让进程退出。
在这里插入图片描述

我们还可以在对信号的自定义处理方式中再定闹钟进行循环

void handler(int signum)
{
    cout<<"闹钟响了"<<endl;
    alarm(5);

}
int main()
{
    signal(14,handler);
    alarm(5);
    while(true)
    {
        cout<<"I am process"<<endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

我们知道每个进程都可能通过alarm接口设置闹钟,所以可能会存在很多闹钟,那么操作系统一定要管理起来它们。
先用一个结构体描述每个闹钟,其中包含各种属性:闹钟还有多久结束(时间戳)、闹钟是一次性的还是周期性的、闹钟跟哪个进程相关、链接下一个闹钟的指针…… 然后我们可以用数据结构把这些数据连接起来。
接下来操作系统会周期性的检查这些闹钟,当前时间戳和结构体中的时间戳进行比较,如果超过了,说明超时了,操作系统就会发送SIGALRM给该进程。

为了方便检查是否超时,可以利用堆结构来管理。

如何理解由软件条件发信号

OS先检测到某种软件条件出发或者不满足,然后构建相关信号发送给对应的进程。

5. 硬件异常产生信号

如何理解除0错误
根据冯诺依曼原理,我们现代计算机在计算时都得要通过cpu来进行运算。
cpu内部有寄存器,寄存器中也有位图,有对应的状态标记位。OS会在计算完成之后对位图进行检测。若发生除0错误,对应的标记位会被置为1,OS进行检测发现后便会找到当前运行的进程,发送相应的信号。
出现硬件异常,进程不一定会退出,一般默认时退出,但我们可以自定义处理行为,但不退出,我们也做不了什么。

我们来看下面这个例子。

oid handler(int signum)
{
    cout<<"除0错误"<<endl;
    sleep(1);
    //alarm(5);

}
int main()
{
    signal(8,handler);
    int a=1;
    a/=0return 0;
}

在这里插入图片描述
我们发现若是,处理处理错误0信号时,我们自定义处理不退出,便会一直进行处理。
这是因为寄存器中的对应的标记位仍然是1,显示异常。OS会不断的去检测,所以才会一直在处理。

如何理解野指针或指针越界问题?
出现这样的错误,其本质都是访问量非法的地址。
所以何时报错,就是何时能够检查出他们访问的地址是非法的。
首先我们在语言层面上的地址都是虚拟地址,其在通过地址找目标位置的时候,虚拟地址会经过页表+MMU映射到物理内存上,而MMU(硬件)就能够判断其地址是否合法,在遇到非法地址,MMU在转化时,便会报错。
OS检测到后便会发送信号给对应的进程,该进程进行后续的处理。

所有信号都有来源,都是通过OS识别,解释,并发送的。

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

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

相关文章

TSINGSEE青犀AI视频识别技术+危化安全生产智慧监管方案

一、背景分析 石油与化学工业生产过程复杂多样&#xff0c;涉及的物料易燃易爆、有毒有害&#xff0c;生产条件多高温高压、低温负压&#xff0c;现场危险化学品存储量大、危险源集中&#xff0c;重特大安全事故多发。打造基于工业互联网的安全生产新型能力&#xff0c;提高危…

storage数据存储问题,不能存undefined

这篇文章分享一下自己使用sessionStorage遇到的一个小问题&#xff0c;以后遇到要避坑。 需求是easyui表格的单元格编辑&#xff0c;点击保存的时候会结束当前行的编辑&#xff0c;然后修改editingId&#xff08;当前编辑行记录的ID&#xff09;。 待解决问题 如图&#xff0c…

操作系统的内存管理之虚拟空间

操作系统的内存管理&#xff0c;主要分为三个方面。 第一&#xff0c;物理内存的管理&#xff0c;相当于会议室管理员管理会议室。 第二&#xff0c;虚拟地址的管理&#xff0c;也即在项目组的视角&#xff0c;会议室的虚拟地址应该如何组织。 第三&#xff0c;虚拟地址和物…

vcomp140.dll丢失是什么意思,vcomp140.dll丢失这几个方法都能修复好

vcomp140.dll是什么&#xff1f; vcomp140.dll是一个动态链接库&#xff08;Dynamic Link Library&#xff09;&#xff0c;它主要用于支持Microsoft Visual C 2015编程语言的运行。这个文件包含了编译器相关的函数和资源&#xff0c;对于使用Visual C 2015开发的程序和游戏来…

Android图形系统之HWComposer、ComposerHal、ComposerImpl、Composer、Hwc2::Composer实例总结(十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

一款Nacos漏洞自动化工具

1、参考GitHub - charonlight/NacosExploitGUI: Nacos漏洞综合利用GUI工具&#xff0c;集成了默认口令漏洞、SQL注入漏洞、身份认证绕过漏洞、反序列化漏洞的检测及其利用 0x01 前言 ​ 本工具已经集成Nacos常见漏洞的检测及其利用&#xff0c;工具为GUI版本&#xff0c;简单…

[迁移学习]DA-DETR基于信息融合的自适应检测模型

原文标题为&#xff1a;DA-DETR: Domain Adaptive Detection Transformer with Information Fusion&#xff1b;发表于CVPR2023 一、概述 本文所描述的模型基于DETR&#xff0c;DETR网络是一种基于Transformer的目标检测网络&#xff0c;详细原理可以参见往期文章&#xff1a;…

k8s 资源预留

KUBERNETES资源管理之–资源预留 Kubernetes 的节点可以按照 Capacity 调度。node节点本身除了运行不少驱动 OS 和 Kubernetes 的系统守护进程&#xff0c;默认情况下 pod 能够使用节点全部可用容量&#xff0c; 除非为这些系统守护进程留出资源&#xff0c;否则它们将与 pod 争…

创造产业链协同优势后,凌雄科技在DaaS行业转动成长飞轮

企业服务领域&#xff0c;一直存在一种共识&#xff1a;做好很难&#xff0c;但一旦服务模式跑通了&#xff0c;得到了市场的认可&#xff0c;要滚起雪球就会事半功倍。 重资产、重运营的DaaS&#xff08;设备及服务&#xff09;赛道&#xff0c;是个非常典型的细分领域。在这…

泡泡玛特首度跨界超跑品牌兰博基尼汽车,以潮流基因探索时空边界

近期&#xff0c;泡泡玛特携手兰博基尼汽车&#xff0c;于上海国际赛车场进行了一场玩味十足的赛道体验。25位兰博基尼车主&#xff0c;及多位汽车领域知名媒体人、kol到场参与。兰博基尼跑车巡游、专业车手驾驶的兰博基尼涂装赛车试乘、MEGA SPACE MOLLY 1000%/400%兰博基尼汽…

深入理解计算机系统CS213学习笔记

Lecture 01 1. 计算机表示数字 int 整数运算可能会出现错误&#xff0c;超过32位时会出现溢出。 float 浮点数不适用结合律&#xff0c;因为浮点数表示的精度有限。 根其原因&#xff0c;是用有限的位数表示无限的数字空间。 2.利用分层的存储系统&#xff0c;使程序运行更…

第三届字节跳动奖学金官宣开奖,13位优秀科研学子每人获10万奖学金

最近&#xff0c;第三届字节跳动奖学金正式公布了获奖者名单。 经过字节跳动技术专家团队层层评审&#xff0c;本届字节跳动奖学金共有来自北京大学、复旦大学、清华大学、上海交通大学、香港科技大学、浙江大学、中国科学技术大学&#xff08;按拼音首字母排序&#xff09;的 …

MyBatis-Plus返回getOne返回null疑惑

getOne返回null 问题描述分析过程总结 问题描述 在数据库建了一张表主要包括两个字段master_id和slave_id;主要的额外字段max_lots 默认值是null&#xff1b;当调用getOne进行查询是&#xff0c;返回是null 分析过程 总结

MFC String类的初始化学习

之前写过CString的用法&#xff1b; VC CString 编程实例图解_bcbobo21cn, cstring-CSDN博客 下面单独看一下CString的各种初始化方式&#xff1b; void CTest2View::OnDraw(CDC* pDC) {CTest2Doc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for nati…

Servlet 初始化参数(web.xml和@WebServlet)

1、通过web.xml方式 <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://xmlns.jcp.org/xm…

三十九、CANdelaStudio实践-19服务(ReadDTCInformation)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的19服务(ReadDTCInformation)编辑,欢迎…

【C++】C++11常见特性

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

基于静电放电算法的无人机航迹规划-附代码

基于静电放电算法的无人机航迹规划 文章目录 基于静电放电算法的无人机航迹规划1.静电放电搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用静电放电算法来优化无人机航迹规划。 …

电脑加密软件哪个好?电脑加密软件推荐

电脑是我们办公离不开的工具&#xff0c;而为了保护电脑数据安全&#xff0c;我们可以使用专业的电脑加密软件来进行加密保护。那么&#xff0c;电脑加密软件哪个好呢&#xff1f;下面我们就来了解一下。 文件加密——超级加密3000 想要安全加密电脑重要文件&#xff0c;我们可…

SAM:Segment Anything 代码复现和测试 基本使用

相关地址 代码&#xff1a; https://github.com/facebookresearch/segment-anything 在线网站&#xff1a; https://segment-anything.com/demo 环境配置 建议可以clone下来学习相关代码&#xff0c;安装可以不依赖与这个库 git clone https://github.com/facebookresearch…