1.认识信号:
信号: 用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
我们通过终端命令查看所有信号
其中1-31我们称为普通信号
34-64是实时信号
2.谁发出了信号?信号是怎么表示的?
其实信号是操作系统发出的,发向目标进程的。 与其说是操作系统发信号,不如说是写信号。
为什么这样说呢?
首先我们先了解一下信号的存储
信号的存储是在相应进程的PCB中的存储的,来看看信号在内核中的表示图
task-sturct就是我们熟悉的PCB,有个指针来维护三张位图,一个比特位代表一个信号
第一个block块表示该信号是否被屏蔽。
第二个pending代表的就是每个信号,用0和1标识是否产生该信号
第三个是一个函数指针数组
3.信号是如何产生的?
信号的产生有多种方法。
1.在终端按下某些组合键,比如ctrl+c 我们会发现进程在运行时按下ctrl+c会立即回到bash。
2.硬件异常产生信号,比如我们的除0操作,当CPU执行除0操作时会检测异常,然后通知内核,内核会发送信号给进程。
3.系统调用,一个进程调用了系统调用kill(2),这时候就可以发送信号给另一个进程,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作时终止进程。
4.终端发送命令kill -n n代表需要终止几号进程
5.软件条产生:比如SIGPIPE信号和SIGALRM信号
4.信号的处理
首先有四点认识
1. 处理信号的前提的认识信号。
2.接受一个信号,并不一定会立即处理,可能会在合适的时候处理。
3.信号的产生对进程是异步的。
4.信号如果无法立即处理,就需要记录下来。 需要注意的是,如果信号被阻塞,则在解除阻塞之前普通信号如果产生多次,只记录一次
信号处理的三种动作
1.执行默认动作
2.忽略默认动作
3.指定自定义动作
接下来我们看看信号的处理过程。
这里递达的意思就是实际执行信号的处理动作,就是信号处理的三种动作。
信号从产生到递达之间的状态叫做未决。
5.信号的阻塞
信号的阻塞就是屏蔽该信号,如果一个信号被阻塞,那么在他解除阻塞之前永远不会被递达。
要注意区别阻塞和忽略,阻塞是不会执行动作,不会递达的意思,而忽略是一种执行动作。
阻塞信号的方法就是修改block块。
还有一点需要注意的上面也提过就是
解除阻塞之前信号产生多次,普通信号只记录一次。
6.信号的捕捉
信号的捕捉就是,如果信号的处理动作是用户自定义函数,在信号递达的时候调用这个函数,这就称为捕捉信号。
用一个简单的例子来说明
#include <iostream>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
using namespace std;
void catch_signal(int sig)
{
cout << "catch a signal is:" << sig << endl;
exit(-1);
}
int main()
{
signal(SIGFPE,catch_signal);
int a = 10;
int b = 0;
int c = a / b;
return 0;
}
运行程序我们发现除0了,但是程序并没有崩溃,因为我们捕捉了SIGFPE信号,一旦硬件异常内核发送信号给进程,我们通过捕捉该信号让他执行我们的自定义动作,而不是默认动作。这就是信号的捕捉。