Linux信号掩码(signal mask)详解与相关例程
本文综合了几个博客中的文章精华:
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;
}
相关文章
- linux tar命令打包整个文件夹详解,Linux tar打包命令详解
- linux load average,Linux Load Average详解
- Linux用户权限:深入解析(linux用户权限详解)
- Linux 命令大全:一本详尽说明手册(linux命令详解手册)
- Linux下禁止自动休眠功能的设置(linux设置不休眠)
- 掌握Linux命令行,轻松输出结果(linux命令行输出)
- Linux下架设网站的步骤详解(linux如何搭建网站)
- Linux cp 命令详解(linuxcp详解)
- Linux查看NFS文件系统详解(linux查看nfs)
- Linux atoi函数:将字符串转换成数字(linuxatoi)
- Linux磁盘挂载情况:一探究竟(linux磁盘挂载情况)
- Linux版:现代操作系统的典范(linux版是什么意思)
- 暗黑2: Linux世界的秘密(linux暗黑2)
- Linux的分支:从灵活性到强大功能(linux的分支)
- Linux应用实用案例详解(linux应用案例精解)
- Linux实现自动登录及自动执行相关操作(linux自动登录并执行)
- Linux .so文件详解:如何安装及使用(linux.so)
- Linux操作技巧:删除行命令详解(linux如何删除行)
- Linux系统运行时间的掌握与管理方法详解(linux运行时间)
- 深入Linux内核:串口配置详解(linux内核串口配置)
- Linux实时内核补丁:保障稳定运行(linux 实时内核补丁)
- 轻松搞定 Linux实用教程:穿件文件命令详解(linux穿件文件命令)
- Linux下SSH安装步骤详解(linux中ssh安装)
- Linux系统:2017的新趋势与发展(linux系统2017)
- Linux精简系统:从这里下载就适合你!(linux精简系统下载)