Linux进程信号的产生、保存、处理

一、信号的概念

信号是一种向目标进程发送消息, 异步通知的一种机制,属于软中断。本质上是用软件来模拟中断行为!

信号在产生之前,操作系统就已经内置了所有信号的识别和处理方式。(通过宏定义出得数字编号识别信号种类;在内核中存在一张handler表,即信号处理函数表。该表的数组下标对应信号编号,数组中存放的内容指向对应信号的处理方式)

 并且信号产生时,操作系统可能在做更重要的事情,也就意味着信号产生之后不会被立即处理,需要被保存。在Linux中,每一个进程PCB中存在一张位图用来保存信号。位图中比特位的位置表示信号种类,比特位内容表示信号的有无!

 进程是无法得知信号何时来的,也就意味着信号的到来和进程执行是异步的。

 同理,在操作系统中已经提前内置了信号信息。我们通过kill -l查看:

在这里插入图片描述

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到。其中 1~31为普通信号,43~64为实时信号(不关心),没有32、33号信号!这些信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal

二、信号的产生

 在Linux中,信号的产生方式分为四大类:键盘终端产生、系统调用产生、硬件异常产生、软件条件产生!

2.1 键盘终端产生

1)、终端信号产生过程

 我们在终端输入诸如ctrl c、ctrl z、ctrl\等指令,我们发现可以将进程终止退出,或者暂停。当我们输入这些指令后,对应硬件会向CPU发送光电信号通知CPU。此时CPU会将这些光电信号转化为中断号,在中断向量表中找到对应硬件的读取方法,将硬件数据读取到内存中。

 当操作系统识别到这些特殊数据后,不会将数据拷贝给目标进程。而是转化为向目标进程发送信号,从而将目标进程退出或暂停等行为!

2)、操作系统得知硬件数据就绪手段

 硬件和进程之间通常会伴随着数据拷贝。比如我们在终端输入指令信息,最后是需要将数据拷贝到内存中,然后看情况是否需要拷贝给目标进程的。当我们输入完指令回车确认后,意味着键盘资源就绪,等待操作系统将数据读走。但操作系统如何的知硬件资源就绪??

 操作系统监测硬件资源就绪,有两种方式:

  • ①:操作系统定期轮询检测所有硬件,判断那些硬件数据就绪。但这非常不现实,如何评判设定轮询间隔时间?时间太长,系统整体效率降低;时间太短会导致CPU疯狂进程监测,减少进程调度次数,进而影响程序性能!
  • ②:另一种方式就是硬件就绪后,让硬件主动通知操作系统!

 首先CPU周围会存在很多针脚,这些针脚对应唯一的一个编号,每一个针脚对应一个唯一硬件。当硬件资源就绪之后,硬件会向CPU发送光电信号。此时CPU检测到光电信号后,会将收到的光电信号转化为对应针脚的编号,并将该数字保存到寄存器中,我们也将这些编号称为中断号

 当OS识别到该寄存器中存在数据时,操作系统就知道存在硬件已经硬件资源就绪了,OS会立马停下手中的所有工作,将对应硬件中的数据加载到CPU中,完成数据的加载。

 为了进一步提高读取硬件数据的效率,操作系统中存在一张函数指针数组,该数组中下标位置对应中断号种类,数组中保存了对应硬件的读取方法!我们将该函数指针数组称为中断向量表,该表会在操作系统初始化时生成!此时操作系统通过中断号即可索引到对应硬件的读取方法,快速将硬件数据读取到内存!!

 当然CPU的针脚是有限的,硬件也不是直接和CPU链接。在CPU和硬件之间通信需要经过主板,而主板存在一定的扩展能力。并且并不是所有的硬件都有发送中断能力!
在这里插入图片描述

3)、 实例:前台进程、后台进程

 下面我们创建一个process.cc源文件,让其死循环输出信息。

#include <iostream>
#include <unistd.h>

int main()
{
   
    int cnt = 0;
    while(true)
    {
   
        std::cout << "running ..." << ++cnt << std::endl;
        sleep(1);
    }
    return 0;
}
  1. 我们编译运行后,产生一个前台进程。我们可以在终端输入ctrl c发送2号信号来终止前台进程!

请添加图片描述
 我们在键盘上按下ctrl c后,会产生硬件中断。操作系统会识别到硬件数据就绪,此时操作系统读键盘上的数据,发送给目标进程。前台进程因为收到2号信号,进而引起信号退出!!

  1. 我们也可以通过ctrl z发送20号信号暂停前台进程!但由于前台进程不能被暂停,否则键盘将失效。此时当前被暂停的前台进程后转化为后台进程。shell外壳进程快速从后台切换为前台进程。

 下面我们将前台进程输入重定向到log.txt,死循环打印消息。然后ctrl z发送20号信号,此时前台进程会变为后台进程。具体效果如下:

请添加图片描述

 我们发现ctrl z向目标进程发送20号信号后,前台进程变为后台进程,并且被暂停!

  1. jobs指令可以查看当前系统中的后台进程。
  2. bg 指令+ 后台进程编号可以重新启动后台进程。fg 指令+ 后台进程编号可以将后台进程提到前台,变为前台进程!
  3. 前台进程只能有一个(键盘只有一个),后台进程可以有多个。两者本质区别在于前台进程可以接收用户输入,后台不行。shell进程比较特殊,不会被ctrl c杀掉。并且根据具体情况,Os会自动将shell提到前台或后台!!

4)、验证终端按键是否产生信号

 上述我们通过终端按键让进程产生一系列行为。当ctrl c真的向目标进程发送了2号信号吗?ctrl z真的向目标进程发送了20号信号吗?我们需要进一步验证!

 操作系统提供了一个signal系统调用即可,可以自定义捕捉信号。

 #include <signal.h>
 
 //函数原型如下,signal()第二个参数用于自定义捕捉信号
 typedef void (*sighandler_t)(int);
 sighandler_t signal(int signum, sighandler_t handler);

下面我们以自定义捕捉2号信号,分别通过终端ctrl c和用户主动发送2号信号,对比进程行为!!

【源代码如下】:自定义捕捉2号信号,让进程受到2号信号退出时,打印一段消息!!

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void handler(int signo)
{
   
    std::cout << "自定义捕捉信号: " << signo << std::endl;
    exit(0);
}

int main()
{
   
    std::cout << "pid: " << getpid() << std::endl;
   
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白debug~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值