Linux网络编程--信号阻塞与屏蔽(block,unblock)


Linux下当向一个进程发出信号时,从信号产生到进程接收该信号并执行相应操作的过程称为信号的等待过程(呃,根据对APUE的理解翻译的)。如果某一个信号没有被进程屏蔽,则我们可以在程序中阻塞进程对该信号所相应的操作。例如一个程序当接收到SIGUSR1信号时会进行一个操作,我们可以利用系统API阻塞(block)程序对该信号的操作,直到我们解除阻止。再举个现实的例子:就好像一个同学让我帮他带饭,但是我现在有其他事要做,现在我先做我手头上的事,直到我把手上的事都完成才去帮他带饭。整个过程差不多就是这样子。

1. sigprocmask函数提供屏蔽和解除屏蔽信号的功能。
从而实现关键代码的运行不被打断。
函数声明如下:

  int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
其中参数 how可设置的参数为:SIG_BLOCK, SIG_UNBLOCK,SIG_SETMASK
SIG_BLOCK:
按照参数 set 提供的屏蔽字,屏蔽信号。并将原信号屏蔽保存到oldset中。
SIG_UNBLOCK:
按照参数 set 提供的屏蔽字进行信号的解除屏蔽。针对Set中的信号进行解屏。
SIG_SETMASK:
按照参数 set 提供的信号设置重新设置系统信号设置。

2. 信号屏蔽与解屏常见实现
方法一: SIG_BLOCK, SIG_UNBLOCK成对实现
优点oldset可以不管。

方法二:
SIG_BLOCK设置屏蔽,保存原有信号设置。
SIG_SETMASK重新恢复原有设置。

3. 屏蔽过程中接受到的信号如何处理
在信号屏蔽过程中,出现的所有被屏蔽的信号,不管发生多少次,在信号解除屏蔽后,系统会执行一次被屏蔽信号上的操作。

下面我们主要考虑多线程与非多线程的情况。
我们来看一段代码,这个程序接收到usr1,usr2信号会设置两个全局变量usr1和usr2的值为1。主程序中有两个循环,第一个循环接收到usr1信号会跳出循环,第二个循环接收到usr1,usr2信号都会跳出循环。

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

int flag_sigusr1 = 0;
int flag_sigusr2 = 0;

void sig_usr1(int signo){
    fprintf(stdout, "caught SIGUSR1\n");
    flag_sigusr1 = 1;
    return;
}

void sig_usr2(int signo){
    fprintf(stdout, "caught SIGUSR2\n");
    flag_sigusr2 = 1;
    return;
}

int main(void){
    sigset_t newmask, oldmask;

    signal(SIGUSR1, sig_usr1);
    signal(SIGUSR2, sig_usr2);

    fprintf(stdout, "first while. catch sigusr1 can break\n");
    while(1){
        if(flag_sigusr1){
            fprintf(stdout, "break");
            break;
        }
        sleep(10);
    }
    flag_sigusr1 = 0;

    //block SIGUSR1
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0){
        perror("sigprocmask error");
    }

    fprintf(stdout, "first while. catch sigusr1 or sigusr2 can break\n");
    while(1){
        if(flag_sigusr1 || flag_sigusr2){
            fprintf(stdout, "break");
            break;
        }
        sleep(10);
    }

    return 0;
}1
第一个循环和第二个循环之间我们选择阻塞sigusr1信号,所以当程序运行到第二个循环时向程序发送usr1信号时并不会跳出循环。

多线程情况下每个线程共用信号处理函数,但是每个线程可以选择自己是否block某个信号。

再看一个多线程的例子:子线程的功能同上,主线程接收到hup信号会向子线程发送usr2信号。

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<pthread.h>

int flag_sigusr1 = 0;
int flag_sigusr2 = 0;
int flag_sighup  = 0;

void sig_usr1(int signo){
    fprintf(stdout, "sig|caught SIGUSR1\n");
    flag_sigusr1 = 1;
    return;
}

void sig_usr2(int signo){
    fprintf(stdout, "sig|caught SIGUSR2\n");
    flag_sigusr2 = 1;
    return;
}

void sig_hup(int signo){
    fprintf(stdout, "sig|caught SIGHUP\n");
    flag_sighup = 1;
    return;
}

void *thread_control_signal(void *arg){
    sigset_t newmask, oldmask;
    sigemptyset(&newmask);

    //thread block sighup
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGHUP);
    if(pthread_sigmask(SIG_BLOCK, &newmask, &oldmask) < 0){
        perror("sigprocmask error");
    } 

    fprintf(stdout, "thread|first while. catch sigusr1 or sigusr2 can break\n");
    while(1){
        if(flag_sigusr1 || flag_sigusr2){
            fprintf(stdout, "thread|break\n");

            break;
        }
        sleep(10);
    }
    flag_sigusr1 = 0;

    //thread block SIGUSR1
    sigaddset(&newmask, SIGUSR1);
    if(pthread_sigmask(SIG_BLOCK, &newmask, &oldmask) < 0){
        perror("sigprocmask error");
    }

    fprintf(stdout, "thread|first while. catch sigusr2 can break\n");
    while(1){
        if(flag_sigusr1 || flag_sigusr2){
            fprintf(stdout, "break\n");
            break;
        }
        sleep(10);
    }

    fprintf(stdout, "thread|thread exit\n");
    return (void *)0;
}

int main(void){
    sigset_t    newmask;
    pthread_t  tid;
    int        signo;

    //signal action
    signal(SIGUSR1, sig_usr1);
    signal(SIGUSR2, sig_usr2);
    signal(SIGHUP , sig_hup);

    if(pthread_create(&tid, NULL, thread_control_signal, NULL) < 0){
        perror("create pthread failed");
        return -1;
    }

 //main thread block sigusr1
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    if(pthread_sigmask(SIG_BLOCK, &newmask, NULL) < 0){
        perror("sigprocmask error");
    }

    //main thread wait sighup
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGHUP);
    if(sigwait(&newmask, &signo) < 0){
        perror("sigwait failed");
        return -1;
    }
    fprintf(stdout, "main|get SIGHUP\n");

    pthread_kill(tid, SIGUSR2);
    pthread_kill(tid, SIGUSR2);
    pthread_join(tid, NULL);

    fprintf(stdout, "main|exit\n");
    return 0;
}

本文永久更新链接地址

相关内容