Linux进程信号之阻塞信号

前两天写了信号的基本概念以及如何去产生信号,欢迎大家戳博客链接:https://blog.csdn.net/apt1203JN/article/details/79955014
先来了解一下信号的三种状态:

* 信号递达(Delivery):实际执行信号的处理动作
* 信号未决(Pending):信号从产生到递达之间的状态
* 信号阻塞(Block):进程可以选择阻塞某个信号,被阻塞的信号产生时将保持在未决状态,直到进程接触对此信号的阻塞,才执行递达的动作

注意阻塞和忽略的不同之处:

* 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在抵达之后可选的一种处理动作

下面来看一下操作系统为每个进程提供的一套信号机制:
这里写图片描述
上图的三张表分别为:阻塞表(Block)、未决表(Pending)、递达表
这三张表分别对应三种不同的状态:信号阻塞、信号未决、信号递达之后的自定义捕捉
前两张表都是通过位图来存储的(决定了当前是否能收到信号),信号被阻塞就将相应位置置1,否则就置0。而在pending表中,当前位是1时表示信号存在,置0时表不存在。(pending表中的数据是判断信号是否存在的重要因素)

在上图当中:

SIGHUP信号(也就是(1)号信号)未阻塞也未产生过,当它抵达时执行默认处理工作。
SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
SIGQUIT信号从未产生过,一旦产生,SIGQUIT信号将被阻塞,它的处理动作是用户自定义的函数singhandler。

小问题:

倘若在进程解除对某种信号的阻塞之前这种信号产生过多次,将会如何处理?

解析:Linux下的实现方式:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

* 小知识点:普通信号允许丢失、实时信号是不允许的

信号集(sigset_t)
因为在上图中,阻塞和未决标志都可以用相同的数据结构(位图)来表示,因此,阻塞和未决标志可以用相同的数据类型(sigset_t)来存储。
sigset_t称之为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态

信号集操作函数

#include <signal.h>
// 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信息
int sigemptyset(sigset_t  *set)
// 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信息包含系统支持的所有信号
int  sigfillset(sigset_t  *set)
int  sigaddset(sigset_t  *setint  signo)
int  sigdelset(sigset_t  *set,     int signo)
int  sigismember(const  sigset_t  *set,   int signo)

注意点:(1)在使用sigset_t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。
(2)初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号

信号屏蔽字(sigprocmask):通过调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)

#include  <signal.h>
int  sigprocmask(int  how,const  sigset_t  *set,sigset_t  *oset);
//  返回值:若成功则为0,若出错则为-1

(1)若oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
(2)若set是非空指针,则更改进程的信号屏蔽字,参数how至少如何更改
(3)若oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字

来看一下how参数的可选值

* SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
* SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set
* SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask=set

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达

#include <signal.h>
sigpending

读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1

下面来写代码看看:

阻塞2号SIGINT信号,让其处于未决状态,去看看信号集数据的变化
代码如下(程序运行,每秒打印各信号的未决状态,让我们来观察一下现象):

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void diaplaysigset(sigset_t  *set)
{
    int i = 0;
    for (; i<32; i++)
      {
            //判断指定信号是否在目标集合中
            if (sigismember(set, i)) 
            {
                  putchar('1');
            }
            else
            {
                  putchar('0');
            }
      }
      puts("");
}
int main()
{
      // 定义信号集对象,并清空初始化
      sigset_t set, oset; 
      sigemptyset(&set);
      // SIGINT:2号信号,相当有ctrl+C
      sigaddset(&set, SIGINT);
      // 设置阻塞信号集,阻塞SIGINT信号
      sigprocmask(SIG_BLOCK, &set, NULL);

      while (1)
      {
          // 获取未决信号集
          sigpending(&oset);
          diaplaysigset(&oset);
          sleep(1);
      }

      return 0;
}

运行之:
这里写图片描述
由上图实现结果我们看到SIGINT信号被阻塞,所以按ctrl+C之后SIGINT信号处于未决状态,此时信号集数据由0表1。因为SIGQUIT信号未被阻塞,所以按ctrl+\终止程序。

猜你喜欢

转载自blog.csdn.net/apt1203JN/article/details/79997246