Linux信号的产生
Linux系列
文章目录
- Linux系列
- 一、信号的产生
- 1.1 异常
- 1.2 alarm()系统调用
- 二 、信号的默认行为
一、信号的产生
上篇文章我们已经介绍了信号的三种产生方式,这部分是对上篇文章的补充
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
调试工具,对错误信息进行,快速定位了。这种调试方法我们称为事后调试。
本篇内容比较乱,主要是用来总结、整合前面的知识的。