初步理解Linux进程信号的处理过程 + Linux常见信号
初步了解进程信号的特点、信号处理函数signal 以及 Linux已有的信号
生活中能看到信号的场景有闹钟、红绿灯、信号枪 ... 等,每当我们看到信号,都会做出对应的反应。进程也是如此,进程具有识别并处理信号的能力。
下面我们需要对进程信号有一个初步的了解,信号是怎么发送的?又是怎么处理的?
目录
一、进程信号的初步理解
1、进程收到信号就会立即处理吗?
进程收到某种信号的时候,并不是立即处理的。比如远处看到红绿灯变成红灯,我们会立即停下吗?并不会,我们会把看到红灯这件事记录在大脑中,等走到路口再停下
进程当前可能在执行优先级更高的东西,所以要选择合适的时候再处理这个信号。
2、没有被立即处理的信号放在哪?
我们看到红灯的时候,会把看到红灯这件事存在大脑中。
既然信号不能被立即处理,已经到来的信号会被暂时保存起来,以供在合适的时候处理,应该保存在哪里呢??——》进程控制块 task_struct
3、谁负责把信号存到指定位置?
信号的本质就是数据,发送信号 ——》向进程控制块 task_struct写入数据 ——》但是进程控制块属于内核,内核不相信任何人,所以由谁来写入数据 ——》 OS!!
4、信号从产生到被处理所经历的过程
信号发送的过程可以通过下面这张图来表示,后面会针对每一步都会作详细的介绍,整个过程一共分成了三步:
(1) 信号发送
信号发送的方式多种多样,可以是键盘发送,如Ctr + C发送2号信号、Ctrl + \ 发送3号信号
也可以是通过命令行指令发送,如
kill -9 进程pid #给对应的进程发送9号信号
注意:第二种方式是通用的方法,2号信号也可以这样发送,如 kill -2 进程pid
(2) 信号保存
前面也提到了,信号是保存在进程控制块里面的,由OS来保存,具体的保存方式是 位图保存
我们常见的信号有31个,也就是前31个,我们可以理解为进程使用无符号32位的整型来保存我们收到的信号(实际上Linux中位图的保存没有这么简单,这个在“信号保存”的博客中也会说明)
当没有收到信号时,所有的比特位都是0,类似于 0000 0000 ....
当我们收到了2号信号时,那就类似于 0100 0000 ....
struct task_struct
{
uint32_t sigs; //0000 0000 0000 ...
}
(3) 信号捕捉和处理
后面会提到一个阻塞的概念,被阻塞的信号无法被捕捉到,既然无法被捕捉,自然就无法被处理,你托中间人给 同事甲 送生日礼物,中间人把礼物丢了,同事甲自然就没法处理礼物了
三、信号处理函数signal
下面要结合代码介绍Linux中常见的信号,所以这里就简单了解一下信号的处理函数signal函数,后面信号捕捉还会再说明一次,signal函数就是建立起某个信号和对应处理函数的连接,等真正接收到这个信号的时候,就会执行对应的处理函数。
第一个参数,是信号的序号,上面已经列出了信号以及对应的序号,既可以用序号,也可以使用信号名
第二个参数,是接收到信号以后处理信号的函数指针
返回值:调用成功,则返回上一次信号处理函数被调用的返回值;调用失败,返回SIG_ERR
因为存在无法找到对应的函数的情况,所以就无法搭建起信号和信号处理函数之间的联系
signal(2,signalhandler); //建立起2号信号和signalhandler函数之间的关系
//等实际收到2号信号时,就会执行signalhandler函数
四、Linux中已有的信号
1、信号总览
我们在命令行输入 kill -l 就可以看到Linux中已经定义好的信号
需要注意的是,看似有64个信号,我们看看黄色下划线部分会发现缺少32、33号,实际上,我们真正要了解的只是前31个信号的一部分,34号以后的,暂不考虑
下面就正式了解前31个信号中的一部分信号
2、2号信号 SIGINT
2号信号的作用是 中止进程,快捷键是Ctrl + C
下面我们用一个小测试来验证一下,下面的代码能接收 前31个信号
一开始我们不发送信号,进程只是在打印内容
当我们 Ctrl + C的时候,OS就会发送2号信号,此时对应的操作除了退出进程外,还会打印一句话,从结果来看,测试成功了,Ctrl + C发送的就是2号信号
3、3号信号 SIGQUIT
3号信号的作用是 终止进程并Core Dump,快捷键是Ctrl + \
Core Dump是指进程异常中止的时候,把进程用户空间的内存数据保存到磁盘上,文件名为core
我们沿用处理2号信号的代码,这里我们就直接展示结果
4、9号信号 SIGKILL(无法被捕捉的信号)
9号信号的作用是 杀死进程同时无法被捕捉到(无法被捕捉到就说明,我们无法修改该信号的默认处理方式)
由于没有快捷键,我们需要新建一个SSH渠道来发送9号信号给当前进程,下面依然沿用2号信号的代码,进程开始运行以后就像下面这样
我们发送9号信号以后,结果进程没有打印回调函数的内容就退出了,因此 9号信号是无法被捕捉的!!
5、8号信号 SIGFPE
8号信号是浮点数异常,比如 1/0的情况
我们稍微修改一下2号信号的代码,故意加入 1/0 的情况,一开始不使用signal函数接收信号,看看进程的默认处理方式
下面加入signal函数,结果发现,实际上我们收到的就是8号信号
6、11号信号 SIGSEGV
11号信号是在 进程崩溃的时候发送的
下面我们稍微修改一下 2号信号的代码,故意让程序崩溃,一开始我们不使用signal函数接收信号,看看程序崩溃时,会有什么反应
很显然,进程崩溃了,下面我们使用signal函数试试,看看我们接收到的是不是11号信号
因此我们可以得出一个结论,当进程崩溃退出的时候,实际上是接收到了第11号信号!
更多推荐
所有评论(0)