zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Linux信号掩码(signal mask)详解与相关例程

Linux 详解 相关 信号 例程 Mask signal 掩码
2023-09-14 09:15:40 时间

本文综合了几个博客中的文章精华:

1. Linux信号掩码(signal mask)_paradox_1_0的博客-CSDN博客_linux信号掩码

2. 信号阻塞与屏蔽:SIG_BLOCK, SIG_UNBLOCK, SIG_MASK区别与使用_中下游国外我的博客-CSDN博客_sig_block

Linux信号掩码简单来说就是如果在某一时刻信号如果包含在进程掩码中,则进程会暂时屏蔽此信号,直到其从进程信号掩码移除为止。

在POSIX下,每个进程有一个信号掩码(signal mask)。简单地说,信号掩码是一个“位图”,其中每一位都对应着一种信号。如果位图中的某一位为1,就表示在执行当前信号的处理程序期间相应的信号暂时被“屏蔽”,使得在执行的过程中不会嵌套地响应那种信号。

为什么对某一信号进行屏蔽呢?我们来看一下对CTRL+C的处理。大家知道,当一个程序正在运行时,在键盘上按一下CTRL+C,内核就会向相应的进程发出一个SIGINT信号,而对这个信号的默认操作就是通过do_exit()结束该进程的运行。但是,有些应用程序可能对CTRL+C有自己的处理,所以就要为SIGINT另行设置一个处理程序,使它指向应用程序中的一个函数,在那个函数中对CTRL+C这个事件作出响应。但是,在实践中却发现,两次CTRL+C事件往往过于密集,有时候刚刚进入第一个信号的处理程序,第二个SIGINT信号就到达了,而第二个信号的默认操作是杀死进程,这样一来第一个信号的处理程序根本没有执行完。为了避免这种情况的出现,就在执行一个信号处理程序的过程中将该种信号自动屏蔽掉。所谓“屏蔽”,与将信号忽略是不同的,它只是将信号暂时“遮盖”一下,一旦屏蔽去掉,后续收到的该信号又继续得到处理。
Linux内核中有一个专门的函数集合来执行设置和修改信号掩码,它们放在kernel/signal.c中,其函数形式和功能如下:

int sigemptyset(sigset_t *mask)                  清空信号集
 
int sigfillset(sigset_t *mask, int signum)       在信号集中设置所有信号  
 
int sigdelset(sigset_t *mask, int signum)        从信号集中删除signum信号
 
int sigaddset(sigset_t *mask, int signum)        在信号集中添加signum信号
 

int sigisnumber(sigset_t *mask, int signum)      测试信号signum是否在信号集中

另外,sigprocmask函数提供屏蔽和解除屏蔽信号的功能。进程也可以利用sigprocmask()系统调用改变和检查自己的信号掩码的值。其实现代码在kernel/signal.c中,原型为:

int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)

其中,set是指向信号掩码的指针,进程的信号掩码是根据参数how的取值设置成set;参数how的取值及含义如下:

SIG_BOLCK

新的进程信号掩码是其当前值和set指定信号集的并集。按照参数set提供的屏蔽字,屏蔽信号。并将原信号屏蔽保存到oldset中。

SIG_UNBOCK

新的进程信号掩码是其当前值和~set信号集的交集,因此set指定的信号集将不被屏蔽。按照参数  set提供的屏蔽字进行信号的解除屏蔽。针对set中的信号进行解除屏蔽。

SIG_SETBLOCK

直接将进程信号掩码设为set。按照参数set提供的信号设置重新设置系统信号设置。

第三个参数oset也是指向信号掩码的指针,它将包含以前的信号掩码值,使得在必要的时候可以恢复它。

进程可以用sigpending()系统调用来检查是否有挂起的阻塞信号。

信号屏蔽与解屏常见实现

方法一: SIG_BLOCK, SIG_UNBLOCK成对实现

优点oldset可以不管。
   
方法二:SIG_BLOCK设置屏蔽,保存原有信号设置。

SIG_SETMASK重新恢复原有设置。

屏蔽过程中接受到的信号如何处理

在信号屏蔽过程中,出现的所有被屏蔽的信号,不管发生多少次,
在信号解除屏蔽后,系统会执行一次被屏蔽信号上的操作。

测试代码与结果对比

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

static void sig_usr1(int signo)
{
  printf("SIGUSR1 function\n");
}
static void sig_usr2(int signo)
{
  printf("SIGUSR2 function\n");
}

static void sig_int(int signo)
{
  printf("SIGINT function\n");
}

void sig_catch(int sig_no, void (*f)(int))
{
    struct sigaction sa;
    sa.sa_handler = f;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(sig_no, &sa, (struct sigaction *) 0);
}

int main(int argc, void **argv)
{
  sigset_t newmask,oldmask;
  
  sig_catch(SIGUSR1,sig_usr1);
  sig_catch(SIGUSR2,sig_usr2);
  sig_catch(SIGINT,sig_int);
   
//  signal(SIGUSR1,sig_usr1);
//  signal(SIGUSR2,sig_usr2);
//  signal(SIGINT,sig_int);
      
  sigemptyset(&newmask);
  sigaddset(&newmask,SIGUSR1);
  sigaddset(&newmask,SIGUSR2);
  sigaddset(&newmask,SIGINT);
  
  sigprocmask(SIG_BLOCK,&newmask,&oldmask);
  printf("SIGUSR is blocked\n");

  kill(getpid(),SIGUSR2);
  kill(getpid(),SIGUSR1);
  kill(getpid(),SIGUSR2);
  kill(getpid(),SIGUSR1);
  kill(getpid(),SIGUSR2);
  kill(getpid(),SIGUSR1);

  printf("SIGUSR is unblocked\n");    

  //方法一    
  sigprocmask(SIG_UNBLOCK,&newmask,NULL);
  
  //方法二
  //sigprocmask(SIG_SETMASK,&oldmask,NULL);

  return 0;
}