Linux 信号(signal):信号的生命周期

  之前简单介绍过信号的生命周期,本文详细聊一聊信号的生命周期:产生、注册、注销、处理。

一、产生

 信号的产生有两种方式:

  • 硬件产生
  • 软件产生

  硬件产生:在程序运行时,我们可以通过键盘的组合键来控制程序终止运行,比如经常使用的 ctrl+c。ctrl+c 其实就是通过硬件给进程发送了一个终止信号,告诉程序终止运行。

  软件产生:我们通常要杀死一个进程使用的是kill命令,kill命令就是给系统发送了一个信号,让进程终止运行。这要注意一点:停止状态的进程是无法被kill杀死的,因为停止状态的进程什么都不干,只有当它开始运行,才会去处理各种事件。

二、注册

  信号的注册就是让进程知道自己接收到了某个信号。比如过马路看到红灯,就代表我们接收到了红灯信号。

  但是进程在收到信号后,不一定会马上处理信号,因此就有了未决信号集合的概念。

1.未决信号

未决信号:

  进程接收到了但暂时没有处理的信号。

未决信号集合:

扫描二维码关注公众号,回复: 15420573 查看本文章

  在Linux中,进程的pcb是task_struct结构体,在进程pcb中存在一个结构体:struct sigpending pending。这个结构体是一个位图,位图中标记了当前进程收到但暂时没有处理的信号,也就是未决信号集合。

位图的作用:

 (1)位图使用二进制比特位存储数据,里面存储的数据是和信号有关的。每个信号在位图中都有自己对应的位置,当进程收到了一个暂时没有被处理的信号,就会把位图中的对应比特位置1。因此这个位图是用来标识是否有信号没有被处理的。

 (2)在pcb中还有一个sigqueue链表,在进程接收到信号,并把位图中对应的比特位置1后,还会把这个信号作为一个结点添加到链表中。在添加的时候,可靠信号与不可靠信号有所不同。可靠信号的注册是可靠注册,不可靠信号的注册是不可靠注册。

2.可靠注册与不可靠注册

(1)可靠注册

  可靠信号(32~64)的注册:不管当前是否有相同信号已经注册,位图都要置1,同时把信号作为结点添加进sigqueue链表。也就是说,链表中可能会存在多个相同的可靠信号

  如图所示:链表中有多个35号信号、37号信号。

可靠

(3)不可靠注册

  不可靠信号(1~31)的注册:如果相同信号没有被注册,则将位图置1,把信号作为结点添加进链表中,否则直接返回。也就是说,链表中不会有相同的不可靠信号

  因此不可靠注册是有缺点的,可能会存在信号丢失的情况。比如这个信号明明通知了两次,但由于是不可靠信号,因此只会记录一次,因此也就只会处理一次。

  如图所示:链表中的信号个数要比信号序列中的信号个数少。

不可靠信号注册

三、注销

  注销信号就是在处理信号之前将信号的存在痕迹抹除。

在处理信号之前抹除信号的原因:

  在多线程的程序中,一个进程可能会有多个线程,如果这个进程收到了一个信号,那么多个线程可能都会去处理。如果是先处理信号再抹除痕迹,当A线程处理信号时,B线程看到有信号,那么也可能会去处理,因此需要在处理信号前先将信号痕迹抹除。

注销信号的流程:

 (1)将sigqueue链表中对应信号的结点删除,然后修改位图中对应的比特位信息。这个过程中,可靠信号与非可靠信号也是有所不同的。

 (2)可靠信号:删除一个要处理的信号结点,但是位图并不是直接修改为0,而是当链表中没有相同的可靠信号结点时才将位图中的对应位置修改为0。

 (3)非可靠信号:删除要处理的信号结点,将位图置0。

四、处理

  处理一个信号实际上就是调用这个信号的处理函数。比如我们过马路时看到红灯(接收到信号),看到后要调用红灯这个信号的处理函数,处理函数就是等待绿灯的到来。

  而在系统中存在一个数组,数组中存储的就是各个信号的处理函数的地址。处理信号时就是根据信号去在这个数组中查找信号对应的处理函数的地址,从而调用处理函数对信号进行处理。

处理函数数组

信号的处理方式有三种:

  1. 默认处理方式:系统中定义好的处理方式。
  2. 忽略处理方式:当这个信号不存在,什么都不做。
  3. 自定义处理方式:用户自己定义一个处理函数,然后替换掉系统中的默认处理函数,让系统处理信号的时候,调用用户自定义的处理函数。

猜你喜欢

转载自blog.csdn.net/weixin_57761086/article/details/128781105