阻塞等待信号
当执行一段不希望被一些信号打断的程序时,可以用下面方式
sigset_t new_set, old_set;
sigemptyset(&new_set);
sigaddset(&new_set,SIGINT);
if(-1 == sigprocmask(SIGBLOCK, &new_set, &old_set))
exit(-1)
/* 受保护代码 */
······
/*************/
/* 恢复信号掩码 */
if(-1 == sigprocmask(SIG_SETMASK, &old_set, NULL))
exit(-1)
pause(); /*等待信号唤醒*/
这就存在一个问题,如果SIGINT信号是在第二个sigprocmask函数之后发送到进程的(虽然这种情况很少),这就回出现立即执行SIGINT的回掉函数,但是pause()函数却还在等待被阻塞的信号的到来。这就和代码原来的想法不太相符。
为了避免这个问题,需要将恢复信号掩码和pause()挂起进程这两个动作封装成一个原子操作,这就是sigsuspend()函数。
阻塞信号
sigsuspend()
函数原形
#include <signal.h>
int sigsuspend(const sigset_t *mask);
参数
mask:指向一个信号集合
返回值
始终返回-1, 并设置errno。通常为EINTR,表示被信号中断,
失败:将error设置为EFAULT
sigsuspend函数会将mask信号集替换进程的信号掩码,然后挂起进程,直到捕获到信号被唤醒(如果捕获的是mask中的信号,则不会被唤醒,继续挂起)、并从信号处理函数返回,一旦从信号处理函数返回,sigsuspend()会将进程的信号掩码恢复成调用前的值。
sigsuspend()函数相当于是不可中断(原子操作)的方式执行一下操作:
sigprocmask(SIG_SETMASK, &mask, &old_mask);
pause();
sigprocmask(SIG_SETMASK, &old_mask, NULL);
正点原子示例
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static void sig_handler(int sig)
{
printf("执行信号处理函数...\n");
}
int main(void)
{
struct sigaction sig = {0};
sigset_t new_mask, old_mask, wait_mask;
/* 信号集初始化 */
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);
sigemptyset(&wait_mask);
/* 注册信号处理函数 */
sig.sa_handler = sig_handler;
sig.sa_flags = 0;
if (-1 == sigaction(SIGINT, &sig, NULL))
exit(-1);
/* 向信号掩码中添加信号 */
if (-1 == sigprocmask(SIG_BLOCK, &new_mask, &old_mask))
exit(-1);
/* 执行保护代码段 */
puts("执行保护代码段");
/******************/
/* 挂起、等待信号唤醒 */
if (-1 != sigsuspend(&wait_mask))
exit(-1);
/* 恢复信号掩码 */
if (-1 == sigprocmask(SIG_SETMASK, &old_mask, NULL))
exit(-1);
exit(0);
}
在上述代码中,我们希望执行受保护代码段时不被 SIGINT 中断信号打断,所以在执行保护代码段之前将 SIGINT 信号添加到进程的信号掩码中,执行完受保护的代码段之后,调用sigsuspend()挂起进程,等待被信号唤醒,被唤醒之后再解除 SIGINT 信号的阻塞状态
等待信号
sigpending
作用:获取正在等待处理的信号的集合
如果进程在执行信号处理函数期间接收到了处在信号掩码中的成员,内核会将其阻塞,将该信号添加到进程等待信号集(等待被处理,处于等待状态的信号)中,可以使用sigpending函数获取正在等待的信号集。
函数原形
#include <signal.h>
int sigpending(sigset_t *set)
函数参数
set:获取到的等待信号集
** 返回值**
成功:0
失败:-1,设置errno
正点原子示例
/* 定义信号集 */
sigset_t sig_set;
/* 将信号集初始化为空 */
sigemptyset(&sig_set);
/* 获取当前处于等待状态的信号 */
sigpending(&sig_set);
/* 判断 SIGINT 信号是否处于等待状态 */
if (1 == sigismember(&sig_set, SIGINT))
puts("SIGINT 信号处于等待状态");
else if (!sigismember(&sig_set, SIGINT))
puts("SIGINT 信号未处于等待状态");