1. 程式人生 > >訊號集與遮蔽訊號

訊號集與遮蔽訊號

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