訊號集與遮蔽訊號
1.訊號集和訊號集處理函式
訊號集是一個位向量,其中每一位對應著linux系統的一個訊號。可使用如下函式對訊號集進行處理:
#include <signal.h>
int sigemptyset(sigset_t * set);
int sigfillset(sigset_t * set);
int sigaddset(sigset_t * set);
int sigdelset(sigset_t * set);
sigemptyset將一個訊號集清空;sigfillset將訊號集的所有位置位;sigaddset函式將引數signo指定的訊號所對應的位設定為1;sigdelset將signo的對應位設定為0。
使用如下函式檢測訊號集的相應位是否被設定:
#include <signal.h>
int sigismember(sigset_t * set,int aigno);
返回值為1時,代表該位被設定,返回值為0時,代表該位未被設定,失敗則返回-1。
//sigset.c 使用訊號集處理函式測試並設定相應訊號的位 #include <stdio.h> #include <signal.h> int main() { sigset_t sig_set; sigemptyset(&sig_set);//清空訊號集 sigaddset(&sig_set,SIGKILL-1);//設定SIGKILL的相應位 if(sigismember(&sig_set,SIGKILL))==1){ printf("SIGKILL has been set/n"); else printf("can't set signal set/n"); return 0; }
2.遮蔽訊號
阻塞一些訊號,使程序即使接收到訊號,也不做處理。
使用如下函式:
#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset); 函式說明 sigprocmask()可以用來改變目前的訊號遮蔽,其操作依引數how來決定 SIG_BLOCK 新的訊號遮蔽由目前的訊號遮蔽和引數set 指定的訊號遮蔽作聯集 SIG_UNBLOCK 將目前的訊號遮蔽刪除掉引數set指定的訊號遮蔽 SIG_SETMASK 將目前的訊號遮蔽設成引數set指定的訊號遮蔽。 如果引數oldset不是NULL指標,那麼目前的訊號遮蔽會由此指標返回。 返回值 執行成功則返回0,如果有錯誤則返回-1。 錯誤程式碼 EFAULT 引數set,oldset指標地址無法存取。 EINTR 此呼叫被中斷
如果set的值為NULL,則無論how是何值都不會更改訊號遮蔽字。這種方法用於得到當前程序的訊號遮蔽字。
sigprocmask(0,NULL,&oset);
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigusr1_handler(int signo)
{
printf("catch SIGUSR1/n");
}
int main(void)
{
sigset_t set;
if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){
perror("can¡¯t set handler for SIGUSR1");
exit(1);
}
sigemptyset(&set);//清空訊號集
sigaddset(&set, SIGUSR1 - 1);//設定SIGUSR1
if(sigprocmask(SIG_BLOCK, &set, NULL) == -1){//遮蔽該訊號
perror("fail to set signal-mask");
exit(1);
}
printf("SIGUSR1 is not available/n");
sleep(10);//休眠,等待使用者傳送SIGUSR1訊號
if(sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //恢復遮蔽的訊號
perror("fail to set signal-mask");
exit(1);
}
printf("SIGUSR1 is available now/n");
sleep(10);//休眠,等待使用者傳送SIGUSR1訊號
return 0;
}
3.處理未決訊號
如果遮蔽了一個訊號,但是程序還是從某處接收到了此訊號,這種訊號叫做未決的。這種訊號是懸而未決的。
如果呼叫sigprocmask後有任何未決的但是已經不再阻塞的訊號時,在該函式返回之前,至少會將這些解放了的未決訊號中的一個傳送給該程序。linux中使用sigpending函式檢查未決訊號,其函式的原型如下;
#include <signal.h>
int sigpending(sigset_t * set);
set表示當前程序中所有未決的訊號。如果成功得到未決訊號集,返回0,否則返回-1。
下例先阻塞SIGUSR1訊號,之後向該程序傳送SIGUSR1訊號,此訊號為未決的,使用sigpending函式測試是否有一個未決的SIGUSR1訊號,最後取消對該訊號的阻塞,處理這個訊號。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigusr1_handler(int signo)
{
printf("catch SIGUSR1/n");
}
int main(void)
{
sigset_t set;
sigset_t sig_pend;
sigemptyset(&set);
sigemptyset(&sig_pend);
if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){
perror("can¡¯t set handler for SIGUSR1");
exit(1);
}
sigaddset(&set, SIGUSR1 - 1); //新增SIGUSR1訊號
if(sigprocmask(SIG_BLOCK, &set, NULL) == -1){ //阻塞SIGUSR1
perror("fail to set signal-mask");
exit(1);
}
sleep(10); //休眠10,期間接收訊號,注意程序休眠後不會被未決的訊號喚醒
if(sigpending(&sig_pend) == -1){ //得到所有的未決訊號集
perror("fail to get pending signal");
exit(1);
}
if(sigismember(&sig_pend, SIGUSR1 - 1) == 1) //測試是否有SIGUSR1訊號是未決的
printf("there is a signal, SIGUSR1, is pending/n");
else{
perror("fail to test signal-set");
exit(1);
}
if(sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //取消對SIGUSR1的阻塞
perror("fail to set signal-mask");
exit(1);
}
printf("SIGUSR1 is available again/n");
return 0;
}
4.高階訊號處理函式
原型:
#include<signal.h> 定義函式 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact); 函式說明 sigaction()會依引數signum指定的訊號編號來設定該訊號的處理函式。引數signum可以指定SIGKILL和SIGSTOP以外的所有訊號。
如引數結構sigaction定義如下
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
sa_handler此引數和signal()的引數handler相同,代表新的訊號處理函式,其他意義請參考signal()。 sa_mask 用來設定在處理該訊號時暫時將sa_mask 指定的訊號擱置。 sa_restorer 此引數沒有使用。 sa_flags 用來設定訊號處理的其他相關操作,下列的數值可用。 OR 運算(|)組合A_NOCLDSTOP : 如果引數signum為SIGCHLD,則當子程序暫停時並不會通知父程序SA_ONESHOT/SA_RESETHAND:當呼叫新的訊號處理函式前,將此訊號處理方式改為系統預設的方式。 SA_RESTART:被訊號中斷的系統呼叫會自行重啟 SA_NOMASK/SA_NODEFER:在處理此訊號未結束前不理會此訊號的再次到來。如果引數oldact不是NULL指標,則原來的訊號處理方式會由此結構sigaction 返回。 返回值 執行成功則返回0,如果有錯誤則返回-1。 錯誤程式碼 EINVAL 引數signum 不合法, 或是企圖攔截SIGKILL/SIGSTOPSIGKILL訊號 EFAULT 引數act,oldact指標地址無法存取。 EINTR 此呼叫被中斷
5.SA_NOCLDWAIT選項
設定SA_NOCLDWAIT選項後,當訊號為SIGCHILD時,則呼叫程序的子程序終止,立即釋放系統資源。如果呼叫程序呼叫wait函式,則會導致該程序阻塞,知道其所有的子程序全部釋放後wait函式返回-1,並將errno錯誤號設定為ECHILD,該選項可以用來避免殭屍程序的產生。
//nowait.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
int main(void)
{
struct sigaction act;
pid_t pid;
act.sa_handler = SIG_DFL;
act.sa_flags = SA_NOCLDWAIT;
act.sa_sigaction = NULL;
sigemptyset(&act.sa_mask);
if(sigaction(SIGCHLD, &act, NULL) == -1){
perror("fail to set handler for SIGCHILD");
exit(1);
}
pid = fork();
if(pid < 0){
perror("fail to fork");
exit(1);
}else if(pid == 0){
printf("the 1st child/n");
exit(0);//第一個程序立即退出
}else{
pid = fork();
if(pid < 0){
perror("fail to fork");
exit(1);
}else if(pid == 0){
printf("the 2nd child/n");
sleep(5);//休眠5秒退出
exit(0);
}else{
if(wait(NULL) == -1)//呼叫wait函式,該函式必定出錯返回
if(errno == ECHILD)
printf("all child quit, no child is zome/n");
printf("the parent/n");
}
}
return 0;
}
執行:
./nowait
the 1st child
the 2nd child
all child quit, no child is zome
the parent
6.SA_NODEFER選項
如果設定SA_NODEFER選項,當捕捉到該訊號時該訊號正在執行處理函式時,不阻塞該訊號,除非sa_mask中指定阻塞該訊號。
//nodefer.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigusr1_handler(int signo)
{
printf("catch SIGUSR1/n");
sleep(5);//等待下一個SIGUSR1訊號
printf("back to main/n");
}
int main(void)
{
struct sigaction act;
act.sa_handler = sigusr1_handler;
act.sa_flags = SA_NODEFER;
act.sa_sigaction = NULL;
sigemptyset(&act.sa_mask);
if(sigaction(SIGUSR1,&act, NULL) == -1){
perror("fail to set handler for SIGCHILD");
exit(1);
}
printf("process begin/n");
sleep(10);//等待SIGUSR1訊號
printf("done/n");
return 0;
}
7.SA_RESETHAND選項
如果設定SA_RESETHAND選項,當訊號處理返回後,該訊號的處理函式乎恢復為預設的訊號處理函式。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigusr1_handler(int signo)
{
printf("catch SIGUSR1/n");
}
int main(void)
{
struct sigaction act;
act.sa_handler = sigusr1_handler;
act.sa_flags = SA_RESETHAND;
act.sa_sigaction = NULL;
sigemptyset(&act.sa_mask);
if(sigaction(SIGUSR1, &act, NULL) == -1){
perror("fail to set handler for SIGCHILD");
exit(1);
}
printf("process begin/n");
sleep(5);//等待第一個SIGUSR1
sleep(5);//等待第二個SIGUSR1
printf("done/n");
return 0;
}
--------------------- 本文來自 阿磊2013 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/ubuntulover/article/details/4428352?utm_source=copy