第8章 信号(5)_信号集和信号屏蔽

3.5 信号集和信号屏蔽

(1)信号集函数:信号集为一个或多个信号的集合,主要用在信号屏蔽函数中

(2)主要的信号集函数

头文件

#include <signal.h>

函数

①int sigemptyset(sigset_t* set);// set为信号集,将信号集清空,对应将所有信号屏蔽字置0.

②int sigfillset(sigset* set); //将所有信号加入到信号集中,对应将所有信号的屏蔽字置1.

③int sigaddset(sigset* set, int signo); //将某个信号加入到信号集中,对应将的信号屏蔽字某位置1.

④int sigdelset(sigset* set, int signo); //将某个信号从信号集中删除,对应将的信号屏蔽字某位置0.

⑤int sigismember(const sigset_t* set, int signo);//测试信号集中是否包含有某个信号,对应判断信号屏蔽字某位是否置1。真返回1,假返回0,出错返回-1。

 

函数

int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);

功能

利用set去覆盖内核中的信号屏蔽字,oldset存放原有的信号屏蔽字

返回值

成功返回0,出错返回-1.

参数

【参数how】:

①SIG_BLOCK:利用set中的信号设置信号屏蔽字(设置某些位的置为1)

相当于mask |= oldest;

②SIG_UNBLOCK:利用set中的信号不设置信号屏蔽字(取消某些位置为0)相当于mask &= ~oldset(先取反,再按位与)

③SIG_SETMASK:利用set信号去替换内核信号屏蔽字。相当于mask = oldest.

备注

(1)进程可以暂时屏蔽信号,使得进程在执行过程中发生的相应信号可以暂时被屏蔽,等待进程解除信号屏蔽后再由内核或驱动将该信号投递给进程

(2)信号屏蔽可屏蔽程序执行过程中的中断

函数

int sigpending(sigset_t* set);

功能

获取信号未决字的内容

返回值

成功返回0,出错返回-1。

【编程实验】查看进程的信号屏蔽字

//signal_mask.c

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void out_set(const sigset_t* set)
{
    int i = 1; //信号是从1开始的
    int bFind = 0;
    for(; i<31; i++){
        //判断信号屏蔽字的某些位置是否置1
        if(sigismember(set, i)){
            bFind |= 1;
            printf("%d ", i); //查看每个信号
        }
    }

    if (bFind) 
        printf("
");
}

void sig_handler(int signo)
{
    printf("begin process the %d
", signo);

    //获得正在处理信号时内核中的信号屏蔽字的内容
    sigset_t oset; //放置内核信号屏蔽字的内容
    //清空信号集oset
    sigemptyset(&oset);
    if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){
        perror("sigprocmask error");
    }
    out_set(&oset); //输出信号屏蔽字的内容
    
    printf("finish process the %d
", signo);
}

int main(void)
{
    if(signal(SIGUSR1, sig_handler) == SIG_ERR){
        perror("signal sigusr1 error");
    }

    if(signal(SIGUSR2, sig_handler) == SIG_ERR){
        perror("signal sigusr2 error");
    }

    sigset_t oset; //放置内核信号屏蔽字的内容
    printf("before signal occured mask:
");
 
    //清空信号集oset
    sigemptyset(&oset);
    //在信号发生前,获得信号屏蔽字的内容
    if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){
        perror("sigprocmask error");
    }
    out_set(&oset); //输出信号屏蔽字的内容

    printf("process %d wait signal...
", getpid()); 
    pause();//进程暂停等待信号
   
    printf("after signal occured mask:
");
    sigemptyset(&oset);
    //在信号发生后,获得信号屏蔽字的内容
    if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){
        perror("sigprocmask error");
    }
    out_set(&oset);

    return 0;
}
/*输出结果:
 [root@localhost]# bin/signal_mask     
 before signal occured mask: //mask为空
 process 2053 wait signal...
 begin process the 12
 12                          //mask为SIGUSR2
 finish process the 12
 after signal occured mask:  //mask为空
 [root@localhost]# bin/signal_mask
 before signal occured mask: //mask为空
 process 2054 wait signal...
 begin process the 10
 10                          //mask为SIGUSR1
 finish process the 10
 after signal occured mask:  //mask为空
 */

(3)信号屏蔽设置

  ①信号在处理过程中是被屏蔽的(被置1),处理完毕解除屏蔽(置0)。可在函数可重入性中利用信号屏蔽技术。

  ②内核中的task_struct中包含两个32位字(记录相关的信号信息),分别是信号屏蔽字blocked和信号未决字pending。其中:

    blocked共有31位信号,0号没有意义,每一位代表一个信号,初始为0。若为0,则该位上发生信号会被立即处理若为1(设置1则信号被屏蔽,设置0则信号不屏蔽),则在该位上发生信号不会被处理,会延迟处理

    pending初始为0,若blocked中某一位为1,但以发生了同样的信号,则在pending同样的位置会被置为1,以便让进程知道该信号又发生过而进行延迟处理

  ③若干个信号一起设置为0或1称为信号集

  ④子进程继承父进程的信号屏蔽字,而不继承信号未决字

【编程实验】利用信号屏蔽来防止函数的重入问题

//signal_reentry2.c

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int  g_v[10]; //全局数组
int* h_v;     //堆数组

void set(int val)
{
    int a_v[10]; //局部数组

    int i = 0;
    for(; i<10; i++){
        a_v[i] = val;
        g_v[i] = val;
        h_v[i] = val;

        sleep(1);
    }

    printf("g_v:");
    for(i=0; i<10; i++){
        if(i != 0)
            printf(",%d", g_v[i]);
        else
            printf("%d", g_v[i]);
    }
    printf("
");
    
    printf("h_v:");
    for(i=0; i<10; i++){
        if(i != 0)
            printf(",%d", h_v[i]);
        else
            printf("%d", h_v[i]);
    }
    printf("
");

    printf("a_v:");
    for(i=0; i<10; i++){
        if(i != 0)
            printf(",%d", a_v[i]);
        else
            printf("%d", a_v[i]);
    }
    printf("
");
}

//信号处理函数
void sig_handler(int signo)
{
    if(signo == SIGTSTP){
        printf("SIGTSTP occured
");
        set(20); //信号处理函数内部再次调用set,以验证函数的
                 //可重入性。注意,传入20
        printf("end SIGTSTP
");
    }
}

int main(void)
{
    if(signal(SIGTSTP, sig_handler) == SIG_ERR){
        perror("signal sigtstp error");
    }

    h_v = (int*)calloc(10, sizeof(int));
    
    printf("begin running main
");
   
    //屏蔽信号(1-31)
    sigset_t sigset;
    sigfillset(&sigset); //屏蔽所有信号
    if(sigprocmask(SIG_SETMASK, &sigset, NULL) < 0){
        perror("sigprocmask error");
    }

    set(10);  //传入10,由于set之前屏蔽了所有信号,所以这时
              //即使按ctrl-z,这个信号也不会马上被捕获,这意
              //味着,执行流没转到信号处理函数,而是正常在main
              //中执行。

    //解除信号屏蔽(如果在屏蔽期间发生信号,会在解释屏蔽之后被发送出去)
    if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0){
        perror("sigprocmask error");
    }

    printf("end running main
");

    free(h_v);
}
/*输出结果:
 [root@localhost]# bin/signal_reentry2
 begin running main   //正常执行流,不产生信号
 g_v:10,10,10,10,10,10,10,10,10,10
 h_v:10,10,10,10,10,10,10,10,10,10
 a_v:10,10,10,10,10,10,10,10,10,10
 end running main
 [root@localhost]# bin/signal_reentry2
 begin running main     //运行2-3秒后,按ctrl-z。注意信号并没有
 ^Zg_v:10,10,10,10,10,10,10,10,10,10  //马上被捕获,而是正常执行main函数
 h_v:10,10,10,10,10,10,10,10,10,10
 a_v:10,10,10,10,10,10,10,10,10,10
 SIGTSTP occured                      //解除屏蔽,信号才被发送过来,从而
 g_v:20,20,20,20,20,20,20,20,20,20    //防止的set函数的重入
 h_v:20,20,20,20,20,20,20,20,20,20
 a_v:20,20,20,20,20,20,20,20,20,20
 end SIGTSTP
 end running main
 */

【编程实验】连续发送同一信号时的信号未决字内容变化

//signal_pending.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void out_set(const sigset_t* set)
{
    int i = 1;
    for(; i<=31; i++){
        if(sigismember(set, i)){
            printf("%d, ", i); //将被屏蔽的信号输出
        }
    }
    printf("
");
}

void sig_handler(int signo)
{
    printf("begin the signal handler
");
    
    int i = 0;
    sigset_t set;
    for(; i<10; i++){       
        printf("(%d)", i + 1);
        
        sigemptyset(&set);
        if(sigpending(&set) < 0){
            perror("sigpending error");
        }else{
            printf("pending signal: ");
            out_set(&set);
        }

        sleep(1);
    }

    printf("end the signal handler
");
}

int main(void)
{
    if(signal(SIGTSTP, sig_handler) == SIG_ERR){
        perror("signal sigtstp error");
    }
    
    printf("process %d wait signal...
", getpid());
    pause(); //进程暂停等待信号
    printf("process finished.
");

    return 0;
}
/*输出结果:
[root@localhost]# bin/signal_pending                             
process 1535 wait signal...
^Zbegin the signal handler  //发送ctrl-z,被唤醒,开始执行信号处理函数
(1)pending signal:          //开始处理信号函数时,信号未决字被清零
(2)pending signal: 
(3)pending signal: 
^Z^Z^Z^Z(4)pending signal: 20, //连续发送4个ctrl-z信号,由于处理信号过程
(5)pending signal: 20,         //中,同一类型的信号被屏蔽。此时再发送的
(6)pending signal: 20,         //同类型信号会被记录在pending中,以便告知
(7)pending signal: 20,         //系统,又有一个同类型的信号等待处理
(8)pending signal: 20, 
(9)pending signal: 20, 
(10)pending signal: 20, 
end the signal handler
begin the signal handler       //处理完第1个信号,开始处理后来未决的信号
(1)pending signal:             //由于是同类型的,被延迟到第1个信号处理完
(2)pending signal:             //毕才开始。
(3)pending signal: 
(4)pending signal: 
(5)pending signal: 
(6)pending signal: 
(7)pending signal: 
(8)pending signal: 
(9)pending signal: 
(10)pending signal: 
end the signal handler          //未决信号被处理完毕,同类型只再处理一次
process finished.
 */
原文地址:https://www.cnblogs.com/5iedu/p/6405193.html