在处理信号时,有些进程不希望被突如其来的信号中断当前的执行,也不想忽略信号,而是希望等到处理完手头上的信号后再处理这些信号,这种情况就涉及到信号的阻塞了。
信号阻塞也称为信号屏蔽。
之前介绍信号的时候列出了64种信号,超过了一个整型数能表示的范围(不能用整型量的每一位代表一种信号),POSIX标准定义了数据类型sigset_t来表示信号集,并且定义了一系列的函数来操作信号集,在shell下输入man sigsettops
可以查看它们的函数原型:
#include
int sigemptyset( sigset_t *set );
int sigfillset( sigset_t *set );
int sigaddset( sigset_t *set, int signum );
int sigdelset( sigset_t *set, int signum );
int sigismemember( const sigset_t *set, int signum );
每个进程都有一个信号掩码,它规定了当前受到阻塞而不能传递给进程的信号集,调用sigprocmask()可以检测或修改进程的信号掩码,函数原型如下:
#include
int sigprocmask( int how, const sigset_t *newset, sigset_t *oldset );
SIG_BLOCK:将newset所指向的信号集中的信号添加到当前信号掩码中作为新的信号屏蔽字
SIG_UNBLOCK:将newset所指向的信号集中的信号从当前的信号掩码中移除
SIG_SETMASK:设置信号掩码为newset所指信号集中的·全部信号
函数sigpending()可以用来获取当前进程中因为被阻塞而不能传递和当前未决的信号集,就是查看有哪些信号被阻塞了,函数原型如下:
#include
int sigpending( sigset_t *set );
函数sigsuspend()主要实现等待一个信号的到来,即将当前进程挂起,函数原型如下:
#include
int sigsuspending( const sigset_t *mask );
通过一个程序熟悉一下信号的阻塞:
#include
#include
#include
/* 出错提示函数,报告出错的函数和出错的行号 */
void error( const char *err_string, int line )
{
fprintf( stderr, "line :%d",line );
perror( err_string );
exit( 1 );
}
/* SIGINT信号处理函数 */
void handler_sigint( int signo )
{
printf("recv SIGIINT\n");
}
int main( void )
{
/* 定义信号集 */
sigset_t newmask, oldmask, pendmask;
/* 安装信号处理函数 */
if( signal( SIGINT, handler_sigint ) == SIG_ERR )
err( "signal", __LINE__ );/* __LINE__返回 */
sleep( 10 );
/* 初始化信号集 */
sigemptyset( &newmask );
/* 将SIGINT添加到newmask信号集中 */
sigaddset( &newmask, SIGINT );
/* 屏蔽信号SIGINT,使用newmask信号集 */
if( sigprocmask( SIG_BLOCK, &newmask, &oldmask ) < 0 )
{
err( "sigprocmask", __LINE__ );
}
else
{
printf("SIGINT blocked\n");
}
sleep( 10 );
/* 获取未决信号队列,存入pendmask中 */
if( sigpending( &pendmask ) < 0 )
{
err( "sigpending",__LINE__ );
}
/* 检查未决信号队列pendmask中是否有SIGINT */
switch( sigismember( &pendmask, SIGINT ) )
{
case 0:
printf("SIGINT is not in pending queue\n");
break;
case 1:
printf("SIGINT is in pending queue\n");
break;
case -1:
err( "sigismember", __LINE__ );
break;
default :
break;
}
/* 解除SIGINT的屏蔽,设置信号掩码为旧的就可以了 */
if( sigprocmask( SIG_SETMASK, &oldmask, NULL ) < 0 )
{
err( "sigprocmask", __LINE__ );
}
else
{
printf("SIGINT unblocked\n");
}
while( 1 )
;
return 0;
}
结果说明:
第一次先发送一个信号,此时还没有将SIGINT添加到信号掩码中,所以在信号处理函数中显示接收到信号。然后进入睡眠,随后将SIGINT信号添加到信号掩码中阻塞信号。然后再次进入睡眠,睡眠期间,多次发送SIGINT信号,此时发送的信号SIGINT是不可靠信号,所以在排队时丢的只剩下一个信号了(可以这样理解),睡眠之后,获取未决信号队列,查看SIGINT信号是否在队列中,输出信息表示SIGINT在未决队列中。然后就解除对SIGINT信号的屏蔽,此时光看程序代码似乎是打印SIGINT unblocked
这句话,但实际上由于未决队列中还有信号(丢的只剩下一个SIGINT信号),所以要先执行未决信号,在返回函数。所以就先执行了一次信号处理函数,随后才打印SIGINT unblocked
。
记住一点:有未决 先执行 再返回