信号原理解析

目录

一、什么是信号

举例子:

进程如何认识信号

信号与进程的异步

进程如何储存信号

二、一个实例

signal函数:

三、实例后的思考

一个进程接受到信号后,处理信号的方法:

myhandler什么时候才会被调用

四、理解ctrl+c被解释成信号的过程


一、什么是信号

举例子:

举一些例子来理解:红绿灯、下课铃、倒计时、狼烟冲锋号、肚子叫、妈妈的脸色、语气、外卖电话……这些都是我们时候中常见的信号。

而且一般来说,我们看到信号就知道我们接下来应该要做什么了比如:红灯停、下课回家、饿了吃饭、外卖到了去拿等等等。

进程如何认识信号

一个进程在运行的时候也会接受到很多的信号,就像上面这些行为一样,收到了一个信号就会去执行一些默认的操作—————这写行为都是程序猿在编写os时就设置的默认操作,这也就是为什么进程会认识这些信号。就比如我像一个进程发送了一个终止信号,他就回去执行终止操作。

信号与进程的异步

给出一个场景,加深一下理解:你在家里玩游戏,你点了一个外卖。玩着玩着你接到了一个外卖电话,让你下去拿外卖,但是你在玩游戏,你说先放在那里,我等一下去拿。

对于进程来说这也是同理的,如果进程在执行一个优先级更高的任务的时候,接收到了信号是不会第一时间去处理的。

因此我们得出一个结论:信号的产生对于进程来说是异步的。

如果信号产生了,我们不第一时间处理的话,这就说明了,进程必须要有储存信号的能力

进程信号时间线:

进程如何储存信号

进程是如何记录对应的产生的信号的呢?储存在哪里?

先看一下常用的对进程信号吧:

观察一下 ,一共有多少个信号呢?许多同学会脱口而出64个,其实不然,31到34之间被分开了。因此这62个信号被分成了两个部分:1~31,34~64。

后面那部分我们不做讨论,那个是实时信号(接受到信号就马上要执行操作),我们的操作系统用不到。

我们会发现,1~31一共31个信号,非常巧合的只比int的比特位少了一个,int有四个字节,一个字节有八个比特位。聪明的同学已经猜到了存放信号的结构————位图。一个int整数每个比特位对应一个信号,0表示没有,1表示有这个信号。

这里也遵循了先描述在组织的原则,int组织。

那么这个int放在哪里的呢————task_struct结构体中。

其实通过上面的描述我们应该也感受出了,1~31号信号,我们只记录它是否产生。

所谓的发送信号,本质上是写入信号,谁写入——由操作系统写入,将0—>1。

二、一个实例

上面说了一大堆,如何证明我们的进程确实收到了信号呢?

先介绍一个函数,再通过一个代码实例:

signal函数:

原型:void (*signal(int signum, void (*handler)(int)))(int);

这里看不懂就跟着我简单了解一下就好了二,会用就行:

外面那一层void *其实就是声明了一个参数为int的函数指针。然后我们是可以直接调用这个函数指针的。然后里面那层看起来就非常正常了:

signal(int signum, void (*handler)(int))

这个函数有两个参数,一个就是信号,另外一个是我们自定义的一个函数的指针

所以这个功能是什么呢:

简单说就是设立一个规则,就是当我们的进程接收到了和第一个参数相等的信号时,就会调用后面我们写的函数指针。

 mykill.cc

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cerrno>
#include<cassert>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>


void myhandler(int signo)
{
    std::cout<<"get a signal:"<<signo<<std::endl;//打印获得的信号
}


int main()
{
    signal(SIGINT,myhandler);
    while(true)
    {
        std::cout<<"我是一个进程,我正在运行"<<getpid()<<std::endl;
        sleep(1);
    }

    return 0;
}

然后我那这个函数在linux终端下运行:

 使用Ctrl+c的原因:用户按下Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程。

简单的说,就是:相当于操作系统给我这个进程写入了一个信号,这个信号就是2——SIGINT信号。

我们发现一般可以直接退出进程的操作无法退出,并继续执行。

我是通过Ctrl+\退出的,这个会被解释成3——SIGQUIT信号。

如果我调皮一点:把这个信号也自定义一下操作呢?

int main()
{
    signal(2,myhandler);
    signal(SIGQUIT,myhandler);//加一个

    while(true)
    {
        std::cout<<"我是一个进程,我正在运行"<<getpid()<<std::endl;
        sleep(1);
    }

    return 0;
}

我们会发现,ctrl+\也不行了。 

我是通过命令行发送信号结束进程的:

 直接将9信号写入18769进程。

三、实例后的思考

一个进程接受到信号后,处理信号的方法:

1、默认动作——就像ctrl+c就是默认退出前台进程。

2、忽略信号——进程可以选择忽略某些信号,这通常是由于进程对该信号无强制要求或者不能有效地处理该信号。使用 signal() 函数可以指定一个信号的处理方式为忽略。

3、用户自定义信号(捕捉信号)——就是用上面的signal函数实现的操作。

这上面三个是我们可以在上面的例子中发现的,还有另外两种情况:

4、阻塞信号:进程可以选择阻塞某些信号的传递,这样当该信号被发送给进程时,操作系统会将其挂起,直到进程取消该信号的阻塞。使用 sigprocmask() 函数可以设置信号掩码来实现信号的阻塞。

5、信号传递:进程可以使用 kill() 函数向其他进程或进程组发送信号,以实现进程间通信或通知其他进程。

myhandler什么时候才会被调用

是在调用singal函数的时候吗?

不是,它只是定下了这个规矩,接收到这个信号的时候才会调用这个函数。

四、理解ctrl+c被解释成信号的过程

这是一个键盘: 

这是一个cpu,上面有很多个引脚 :

 这些引脚连接不同的硬件,其中就好引脚连接了我们的键盘:

 当键盘被摁下,cpu内部就会储存一个中断号,这里是9。

 然后cpu就会从一个向量中断表(一个数组)里去寻找一个下标为9的元素:

 然后这个元素就会指向一个读取键盘的方法:

 这样就读取到了一个ctrl+c的数据,然后将2号信号写入对应的进程就可以了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一周学八天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值