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;
}