一、sigprocmask
  • 功能:用来检测或修改信号屏蔽字,或者同时检测和修改信号屏蔽字
  • 注意事项(重点):sigprocmask仅为单线程定义,多线程使用另一个函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);

参数:

  • 检测信号屏蔽字功能:如果oset是非空指针,进程的当前信号屏蔽字通过oset返回
  • 修改信号屏蔽字功能:如果set是一个非空指针, 则参数how指示如何修改当前信号屏蔽字
  • 如果set是个空指针,则不改变该进程的信号屏蔽字, how的值也无意义

how取值如下:

SIG_BLOCK(或操作)

该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集

set包含了我们希望阻塞的附加信号

SIG_UNBLOCK

该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集的交集

set包含了我们希望解除阻塞的信号

SIG_SETMASK(赋值操作 ) 该进程新的信号屏蔽是set指向的值

返回值:

  • 若成功则为0,若出错则为-1并设置errno

演示案例

  • 打印调用该函数的进程信号屏蔽字中的信号名 
#include <errno.h>
#include<stdio.h>
#include<signal.h>
void pr_mask(const char *str);

int main()
{
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGINT);
    sigprocmask(SIG_SETMASK,&sigset,NULL);
    
    pr_mask("I am sigpromask.c");
}
void pr_mask(const char *str)
{
    sigset_t sigset;
    int errno_save;
    errno_save = errno; /* we can be called by signal handlers */
    
    if (sigprocmask(0, NULL, &sigset) < 0) {
        perror("sigprocmask error");
    } 
    else {
    printf("%s\n", str);
    
    if (sigismember(&sigset, SIGINT))
        printf(" SIGINT");
    if (sigismember(&sigset, SIGQUIT))
        printf(" SIGQUIT");
    if (sigismember(&sigset, SIGUSR1))
        printf(" SIGUSR1");
    if (sigismember(&sigset, SIGALRM))
        printf(" SIGALRM");

    printf("\n");
    }
    errno = errno_save;
}
  • 演示结果(程序中信号屏蔽字中设置了SIGINT信号为阻塞信号)

APUE编程:79---信号处理(sigprocmask、sigpending信号屏蔽字的检测、修改函数)_位域

二、sigpending
  • 功能:返回一个阻塞信号集,信号集中的信号都是阻塞的 
#include <signal.h>
int sigpending(sigset_t *set);
  • 参数:保存进程的阻塞信号集
  • 返回值:若成功则为0,若出错则为-1并设置errno

演示案例 

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
static void sig_quit(int);

int main(void)
{
    sigset_t newmask, oldmask, pendmask;
    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
        perror("can’t catch SIGQUIT");

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);
    //将SIGQUIT信号设置为阻塞的,同时保存旧的信号屏蔽字,此屏蔽字没有SIGQUIT信号
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        perror("SIG_BLOCK error");
    sleep(5);
    
    if (sigpending(&pendmask) < 0)
        perror("sigpending error");
    if (sigismember(&pendmask, SIGQUIT))
        printf("\nSIGQUIT pending\n");
    
    //将旧的信号屏蔽字给该进程,从而解除对SIGQUIT信号的阻塞
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        perror("SIG_SETMASK error");
    
    printf("SIGQUIT unblocked\n");
    sleep(5);
    exit(0);
}

static void sig_quit(int signo)
{
    printf("caught SIGQUIT\n");
    if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
        perror("can’t reset SIGQUIT");
}

演示结果

  • SIGQUIT信号是键盘输入crrl+\产生的
  • 我们在第一次sleep(5)之前输入ctrl+\,然后产生的SIGQUIT信号被设置为阻塞的
  • 在sigprocmask(SIG_BLOCK, &newmask, &oldmask)函数中将阻塞的SIGQUIT信号移除了,SIGQUIT信号不在阻塞,从而进程接收到,然后打印caught SIGQUIT,最后再打印SIGQUIT unblocked

APUE编程:79---信号处理(sigprocmask、sigpending信号屏蔽字的检测、修改函数)_赋值_02

  • 我们第二次输入了多个ctrl+\,但是SIGQUIT信号接触阻塞后,进程只捕获一个SIGQUIT信号,因此不会对阻塞的信号进行排队
三、sigprocmask、sigpending综合使用
  • 程序运行10秒之前,如果有SIGINT信号传进来,就将SIGINT信号屏蔽字,10秒之后取消对SIGINT信号的屏蔽
  • 并且每秒都打印一次当前进程的信号屏蔽字
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<sys/types.h>

void handler(int sig)
{
    printf("get a sig,num is:%d\n",sig);
}

void print_sig(sigset_t *p)
{
    int i = 1;
    for(;i < 32;++i){ //打印。循环打印位域,看看是哪一个信号
        if(sigismember(p,i)){
            printf("1");
        }else{
            printf("0");
        }
    }
    printf("\n");
}

int main()
{
    signal(2,handler);      //等待SIGINT信号

    sigset_t s,p,o;
    sigemptyset(&s);
    sigemptyset(&o);
    sigaddset(&s,SIGINT);

    sigprocmask(SIG_SETMASK,&s,&o); //将SIGINT信号屏蔽

    int count = 0;
    while(1){
        sigemptyset(&p);
        sigpending(&p);  //获取当前进程的屏蔽字
        print_sig(&p);   //打印信号屏蔽字
        sleep(1);        //休眠1秒
        if( count++ == 10 ){//休眠10秒之后,将SIGINT信号取消屏蔽
            sigprocmask(SIG_SETMASK,&o,NULL);
            printf("recover block\n");
            sleep(3);
        }
    }
}

APUE编程:79---信号处理(sigprocmask、sigpending信号屏蔽字的检测、修改函数)_位域_03

演示结果

  • 前10秒之前接收到ctrl+c信号之后,可以看到比特位第2位变为1,SIGINT被屏蔽。10秒之后取消阻塞