Linux信号的产生

Linux系列



一、信号的产生

上篇文章我们已经介绍了信号的三种产生方式,这部分是对上篇文章的补充

1.1 异常

在编写程序时,我们的程序经常会出现如:除零错误、野指针错误,导致进程崩溃,而进场崩溃的根本原因就是因为OS向我们的进程发送了信号,下面我们通过两个例子详细分析:

例一

#include<iostream>

using namespace std;
int main()
{
    int a=10;
    int b=0;
    int c=a/b;
    cout<<"sucess!!!"<<endl;
    return 0;
}

在这里插入图片描述
可以看到当程序发生除零错误后,直接崩溃不会再执行下面代码。

在这里插入图片描述
上图信息通过:man 7 signal 查找得到
从上图可以看出进程是接受到八号信号,而退出的,下面我们通过捕捉来验证:

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;

void handler(int numsig)
{
    cout<<"I capture a signal:"<<numsig<<endl;
    sleep(1);
    return ;
}
int main()
{
    signal(8,handler);
    int a=10;
    int b=0;
    int c=a/b;
    return 0;
}

在这里插入图片描述
我们能看到进程确实收到了8号信号,但是我们并没有写循环为什么handler方法一直被执行呢?OS又是如何判断要给进程发送8号信号的?
在这里插入图片描述
CPU中存在很多寄存器,当CPU执行程序,执行到除零运算时,状态寄存器的溢出标志位被置位(1),CPU发生硬件中断产生中断号,被OS识别,操作系统根据中断向量表给进程发送信号,信号被进程捕捉执行我们自己的方法,执行完毕返回进程并没有被退出,进程继续被调度,但是进程上下文数据并没有修改,溢出标志位一直为1,OS 一直发送信号…,这样就表现为handler方法一直被调度。

例二

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;

// void handler(int numsig)
// {
//     cout<<"I capture a signal:"<<numsig<<endl;
//     sleep(1);
//     return ;
// }
int main()
{
    //signal(8,handler);
    int *p=nullptr;//野指针
    *p=2;
    return 0;
}

在这里插入图片描述
当进程发生野指针错误时,进程也会直接崩溃,此时进程接收到的是11号信号,你可以自己查看这里就不展示了。

在这里插入图片描述
当你对该信号捕捉执行自定义方法,如果不退出进程,结果依然表现为handler方法一直被调用。

在这里插入图片描述
当我们内存访问时,CPU中存在存在一个MMU内存管理单元,帮助我们把虚拟地址转化为物理地址,当转化失败(对该内存没有权限、不存在等)时,就会将转化失败的虚拟内存地址存放到CPU寄存器中,OS检测到后给进程发送信号。我们对于异常的处理,一般都为终止进程,并不会尝试修复它,而这些异常信号可以被捕获,是OS方便用户对数据进行处理(保存、日志等)的。

上面的两个例子都是由硬件产生的,但是异常还可以由软件产生,这点我在管道部分介绍过了,下面会直接用作示例来介绍新的知识。

1.2 alarm()系统调用

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

功能:设定一个定时器,在指定的秒数后向进程发送SIGALRM信号。

参数

  • seconds:如果不为0在经过seconds秒后,进程会收到SIGALRM信号。如果在定时器到期之前再次调用alarm函数,会重新设置定时器,之前剩余的时间被忽略,如果为0,定时器被取消,之前设置的任何未到期的定时器都会被清除,且不会发送信号。

返回值

  • 返回上一次设置定时器的剩余秒数,如果之前设置的定时器已经过期返回0.

示例:

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;

void work()
{
    cout<<"起床了!!!"<<endl;
}
void handler(int numsig)
{
    work();//执行闹钟任务
    return ;
}
int main()
{
    signal(SIGALRM,handler);//捕捉信号,执行自定义任务
    alarm(5);//5秒后给进程发送SIGALRM信号
    int cnt=0;
    while(1)
    {
        cout<<cnt++<<endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
可以看到当我们的程序设置闹钟后,就可以通过捕捉信号,完成指定任务的执行行,这种方式我们一般用来格一段时间,来打印一次日志信息。

这个别看:闹钟什么时候触发依赖于时间戳,对闹钟进行管理。。填充闹钟结构体属性。。。管理闹钟结构体。。系统维护当前时间。。。对比是否超时。。。堆结构,提高比较效率。。。。

二 、信号的默认行为

在这里插入图片描述

  • Term:表示该信号的默认行为是终止进程。

  • Core:意味着信号触发时,进程在终止的同时,会产生核心转储(Core Dump)文件,用于后续调试分析。

在我们介绍进程控制时,就遇到过了这个问题:

在这里插入图片描述
下面我们来尝试获取这个标志,另外我们需要先将服务器Core 权限打开
在这里插入图片描述
在这里插入图片描述
后面就需要根据你自己的系统配置了,大家可以结合自己的系统搜索以下,我们直接跳入使用部分:
在这里插入图片描述
这样我们就可以直接使用,生成的转储文件,结合gdb调试工具,对错误信息进行,快速定位了。这种调试方法我们称为事后调试。

本篇内容比较乱,主要是用来总结、整合前面的知识的。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值