信号–Signal
信号的作用就相当于神经系统,来帮助操作系统进程运行过程中遇到的各种意外。
在
Linux
系统中通过命令kill -l
来查看所有信号。
信号产生的方式:
1. 通过终端按键产生信号(键盘产生)
最常见的键盘产生的信号:
ctrl + c => 2
对应的是2号信号SIGINT
。
ctrl + z => 19
对应的是19号信号SIGSTOP。
ctrl + \ => 3
对应的是3号信号SIGQUIT。
2. 硬件异常产生信号
-
MMU产生信号 => 段错误
我相信大家对段错误已经很熟悉了,就是越界访问嘛。可是段错误的底层原理到底是什么呢?
当程序传来一个非法的地址的时候,MMU就先从页表中寻找相对应的物理地址,当MMU没有找到,MMU就会告诉操作系统虚拟地址非法,由操作系统给该进程发送一个11号信号
SIGSEGV
让进程异常终止,引发段错误。#include <iostream> int main(void) { int *p = nullptr; *p = 7; std::cout << *p << std::endl; return 0; }
-
CPU 产生信号 => 除0操作
CPU是一个强大的运算器,当CPU在运算时遇到了除 0 操作,就会告诉操作系统这是一个除 0 操作,操作系统就会给该进程发送一个 8 号信号
SIGFPE
。#include <iostream> int main(void) { std::cout << 6 / 0 << std::endl; return 0; }
3. 软件条件产生信号
-
assert 表达式如果为假,就会调用
abort()
函数,abort()
函数就会触发6号信号SIGABRT
。#include <iostream> #include <cassert> int main(void) { assert(0); return 0; }
-
PIPE
读端关闭,如果尝试写,就会触发SIGPIPE
信号,管道破裂。出现的时候没有任何征兆,没有提示。
4. 调用系统函数,向进程发送信号
kill
命令是调用kill
函数实现的。kill
函数可以给一个指定的进程发送指定的信号。
raise
函数可以给当前进程发送指定的信号。
信号处理方式
-
忽略。
这就表示当接收到信号后,选择忽略这个信号,就是不做任何处理。这是一种不好的处理方式。
-
默认处理行为。
大部分的信号默认处理行为都是结束进程。
-
捕捉信号。
通过自定义的行为来处理信号。
信号捕捉
捕捉信号就是当操作系统给该进程传来一个信号,我们可以通过自己定义的处理方式来处理。
#include <iostream>
#include <cassert>
#include <csignal>
void MyHandler (int signal)
{
std::cout << "Catch a signal : " << signal << std::endl;
}
int main(void)
{
signal(SIGINT, MyHandler);
while (1)
{
;
}
return 0;
}
当我通过键盘ctrl + c
来触发2号信号的时候,signal
系统调用就会捕捉到2号信号,并且对2号信号做出我自定义的行为而不是默认的终止程序了。
那么信号的捕捉到底是怎么执行的呢?
- 首先不管程序执行到哪个地方了,当
singnal
捕捉到了一个信号,就会立刻触发信号。 - 然后到内核中,根据信号的种类来进行处理。
- 执行我们自定义的回调函数。
- 回调函数结束后,会继续到刚才程序执行到的地方的继续执行程序。
叮~ ?