异步处理方式之信号(二):可靠的信号与不可靠的信号

不可靠的信号

4.1 什么是不可靠的信号

​ 在早期的Unix版本中,信号是不可靠的。这里的不可靠指的是:信号可能会丢失(一个信号已经发生了,但是该进程却不知道这一点)。除此之外,进程对信号的控制能力也特别差,它只能捕捉或者忽略信号。但是有时用户希望通知内核阻塞某个信号,不要忽略该信号;而在进程准备好处理该信号时在由内核重新通知该进程。

某些书籍提到signal函数每触发一次,得重新调用signal重新注册安装信号处理函数。这已经很很久以前的了,现在是一次signal注册,之后多次使用,无需每次在信号处理函数中重新调用signal函数(Linux便是如此)

4.2 信号丢失的例子:

​ 正常情况下,信号的发生频率很低,因此信号丢失的情况比较少。但是如果信号发生的频率比较高,且信号处理函数费时的话就很容易发生信号丢失的情形。下面我们通过在信号处理函数中调用延时函数模拟实现:

/*************************************************************************
             > File Name: signal_lost.c
             > Author: Toney Sun
             > Mail: [email protected]
       > Created Time: 2020年04月27日 星期一 15时03分18秒
 ************************************************************************/

#include<stdio.h>
#include<signal.h>

extern void my_msleep(int mseconds);//自己实现的睡眠函数,与库函数并无区别

static void sig_handler(int signo)
{
      static int flag=0;
      printf("sig_handler finish: flag=%d\n", flag);
      if(signo == SIGINT){
            printf("Recieved SIGINT signal\n");
            flag++;
            my_msleep(5000);//延时5秒
      }else{
            printf("sig_handler receieve Error signal\n");
      }
      printf("sig_handler finish: flag=%d\n", flag);
}
int signal_lost_test()
{
     if(signal(SIGINT, sig_handler)==SIG_ERR){
           printf("SIGINT handle function register error\n");
     } 
}

执行结果如下:

toney@ubantu:/mnt/hgfs/em嵌入式学习记录/schedule调度器$ ./demo.out 
^Csig_handler finish: flag=0
Recieved SIGINT signal
^C^C^Csig_handler finish: flag=1
sig_handler finish: flag=1
Recieved SIGINT signal
sig_handler finish: flag=2

运行程序后我是通过连续按下四次“Ctrl+C”,产生四个SIGINT信号,但实际上只捕获了其中的两个信号,另外两个则丢失了。之所以说丢失,是因为程序只打印了两次信号处理函数中的信息,该进程也只知道发生了两次事件。出现这种情形的主要原因在于信号处理程序处理的太慢(程序中我们把她睡了会儿),而信号又发生的太频繁,CPU处理不过来导致的。

​ 此外,上述结果中还有个奇怪的现象:信号处理程序不是一次性执行完毕的(专业点称为存在竞态),而是在第一个执行过程中又去相应下一个信号处理函数,等下一个处理完毕了再回来继续处理先前未执行完毕的信号处理函数。这个现像应该在编写程序中特别注意下。

5. 可靠的信号

​ 在第4部分,我们简要的说明了下什么是不可靠的信号,这里我们再来简要的说明下什么是可靠的信号。

5.1 常见的术语

  • 递送: 当对信号采取某种动作时,我们说向进程递送了一个信号。
  • 未决的:在信号产生(generate)和信号递送之间的这段时间间隔内,称信号为未决的(pending)

5.2 可靠的信号说明

​ 进程可以选择使用“阻塞信号递送”。如果为进程产生了一个阻塞的信号,而且对信号的动作是系统默认动作或捕捉该信号(非忽略状态),则为该进程将此信号保持为未决状态,直到该进程对此信号接触阻塞状态(或者修改为忽略此信号)。

​ 内核在递送一个原来被阻塞的信号给进程时,才决定对它的处理方式(因此之前的状态称为未决的)。于是乎进程在信号递送之前是可以修改对该信号的动作。系统可以调用sigpending函数来判断哪些信号是设置为阻塞同时悬而未决的。

​ 如果进程在解除多某个信号阻塞之前,该信号已经发生了多次(就是我们4.2的例子),那么该如何处理呢?目前大多数系统仍然是只递送一次该信号,也就是说不支持信号排队

​ 每一个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞递送到该进程的信号集。对于每一种可能的信号,该屏蔽字中都有与之相对应的位。如果该位被设置,则当前进程会阻塞该信号。程序中可以使用sigprocmask(后面我会详细说明)来查询和设置当前进程的信号屏蔽字。

​ 信号编号可能会超过一个整数所包含的二进制位数(32位系统是32位),因此POSIX.1专门定义了一个新的类型sigset_t,它可以容纳一个信号集。信号屏蔽字就存放在其中一个信号集中。后面我们对这部分进行详细说明。

原创文章 96 获赞 73 访问量 6万+

猜你喜欢

转载自blog.csdn.net/s2603898260/article/details/105793208
今日推荐