信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。
1 信号集
所有的信号阻塞函数都使用称作信号集的数据结构来表明受到影响的信号。每一个操作都包括两个阶段:创建信号集,传递信号集给特定的库函数。下面说明信号集和相关的数据类型:
sigset_t:这个数据类型用来代表信号的集合,有两种方法对它进行初始化。一种是通过函数sigemptyset()使之不包含任何信号,然后用 sigaddset()函数加入需要的信号。另一种方法是通过函数sigfillset()使之包含所有信号,然后通过sigdelset()函数删除我们不需要的信号。注意,千万不用试图通过手工方式直接操作这种类型变量,否则会带来严重的错误。下面介绍相关的函数。
int sigemptyset(sigset_t *set):初始化信号集set使之不包含任何信号,这个函数总是返回0。
int sigfillset(sigset_t *set):初始化信号集set使之包含所有的信号,这个函数也是总返回0。
int sigaddset(sigset_t *set, int signum):该函数把信号signum加入到信号集set中,需要注意的是这个函数只是修改了set变量本身,并不作其它操作。该函数成功操作返回 0,失败返回-1,错误代码设置成EINVAL,表示signum不是有效的信号代码。
int sigdelset(sigset_t *set, int signum):该函数从信号集set中删除信号signum,其它方面和sigaddset()函数类似,不再赘述。
int sigismember(const sigset_t *set,int signum):这个函数测试信号signum是否包含在信号集合set中,如果包含返回1,不包含返回0,出错返回-1。错误代码也只有一个 EINVAL,表示signum不是有效的信号代码。
2. 进程的信号掩码
我们称正在阻塞的信号的集合为信号掩码(signal mask)。每个进程都有自己的信号掩码,创建子进程时子进程将继承父进程的信号掩码。我们可以通过修改当前的信号掩码来改变信号的阻塞情况。
int sigprocmask(int how, const sigset_t *set,sigset_t *oldset),该函数用来检查和改变调用进程的信号掩码,其中的how参数指出信号掩码改变的方式,必须是下面的值之一:
SIG_BLOCK,阻塞set中包含的信号。意思是说把set中的信号加到当前的信号掩码中去,新的信号掩码是set和旧信号掩码的并集。
SIG_UNBLOCK,解除set中信号的阻塞,从当前信号掩码中去除set中的信号。
SIG_SETMASK,设置信号掩码,既按照set中的信号重新设置信号掩码。
最后一个参数是进程原来的信号集。如果你只需要改变信号的阻塞情况而不需要关心原来的值,可以传递NULL指针给函数。如果你希望什么也不改变,只是想获得当前信号掩码的信息,那么把set设置成NULL,old中返回当前的设置。
sigprocmask()函数成功返回0,失败返回-1。失败时错误代码只可能是EINVAL,表示参数how不合法。
不能阻塞SIGKILL和SIGSTOP等信号,但是当set参数包含这些信号时sigprocmask()不返回错误,只是忽略它们。另外,阻塞 SIGFPE这样的信号可能导致不可挽回的结果,因为这些信号是由程序错误产生的,忽略它们只能导致程序无法执行而被终止。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
int main()
{
sigset_t intmask;
int i;
sigemptyset(&intmask); //清空信号集
sigaddset(&intmask,SIGINT); //将SIGINT加入信号集
while(1)
{
fprintf(stdout,"SIGINT signal blocked\n");
sigprocmask(SIG_BLOCK,&intmask,NULL); //阻塞这个信号
sleep(10); //这10秒内,用ctrl+c无效
fprintf(stdout,"SIGINT signal unblocked\n");
sigprocmask(SIG_UNBLOCK,&intmask,NULL); //解除阻塞
sleep(10); //这10秒内,用ctrl+c 有效
}
return 0;
}