signal信號和sigaction信號處理機制

1. signal信號處理機制

可以用函數signal註冊一個信號捕捉函數。原型爲:

#include 
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);

   signal的第1個參數signum表示要捕捉的信號,第2個參數是個函數指針,表示要對該信號進行捕捉的函數,該

參數也可以是 SIG_DEF(表示交由系統缺省處理,相當於白註冊了)或SIG_IGN(表示忽略掉該信號而不做任何處

理)。signal如果調用成功,返回以前該信 號的處理函數的地址,否則返回SIG_ERR。

   sighandler_t是信號捕捉函數,由signal函數註冊,註冊以後,在整個進程運行過程中均有效,並且對不

同的信號可以註冊同一個信號捕捉函數。該函數只有一個參數,表示信號值。

   示例:

捕捉終端CTRL+c產生的SIGINT信號:

#include 
#include 
#include 
#include
void SignHandler(int iSignNo)
{
    printf("Capture sign no:%d\n",iSignNo);
}
int main()
{
    signal(SIGINT,SignHandler);
    while(true)
        sleep(1);
    return 0;
}

該程序運行起來以後,通過按CTRL+c將不再終止程序的運行。應爲CTRL+c產生的SIGINT信號已經由進程中注

冊的SignHandler函數捕 捉了。該程序可以通過Ctrl+\終止,因爲組合鍵Ctrl+\能夠產生SIGQUIT信號,而該信

號的捕捉函數尚未在程序中註冊。

忽略掉終端CTRL+c產生的SIGINT信號:

#include 
#include 
#include 
#include
int main()
{
    signal(SIGINT,SIG_IGN);
    while(true)
        sleep(1);
    return 0;
}

該程序運行起來以後,將CTRL+C產生的SIGINT信號忽略掉了,所以CTRL+C將不再能是該進程終止,要終止

該進程,可以向進程發送SIGQUIT信號,即組合鍵CTRL+\

接受信號的默認處理,接受默認處理就相當於沒有寫信號處理程序:

#include 
#include 
#include 
#include
int main()
{
    signal(SIGINT,DEF);
    while(true)
        sleep(1);
    return 0;
}

2. sigaction信號處理機制

2.1. 信號處理情況分析

在signal處理機制下,還有許多特殊情況需要考慮:

冊一個信號處理函數,並且處理完畢一個信號之後,是否需要重新註冊,才能夠捕捉下一個信號;

如果信號處理函數正在處理信號,並且還沒有處理完畢時,又發生了一個同類型的信號,這時該怎麼處理;

如果信號處理函數正在處理信號,並且還沒有處理完畢時,又發生了一個不同類型的信號,這時該怎麼處理;

如果程序阻塞在一個系統調用(如read(…))時,發生了一個信號,這時是讓系統調用返回錯誤再接着進入信號處

理函數,還是先跳轉到信號處理函數,等信號處理完畢後,系統調用再返回。

示例:

#include 
#include 
#include 
#include
int g_iSeq=0;
void SignHandler(int iSignNo)
{
    int iSeq=g_iSeq++;
    printf("%d Enter SignHandler,signo:%d.\n",iSeq,iSignNo);
    sleep(3);
    printf("%d Leave SignHandler,signo:%d\n",iSeq,iSignNo);
}
int main()
{
    char szBuf[8];
    int iRet;
    signal(SIGINT,SignHandler);
    signal(SIGQUIT,SignHandler);
    do{
        iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);
        if(iRet<0){
            perror("read fail.");
            break;
        }
      szBuf[iRet]=0;
        printf("Get: %s",szBuf);
    }while(strcmp(szBuf,"quit\n")!=0);
    return 0;
}

程序運行時,針對於如下幾種輸入情況(要輸入得快),看輸出結果:

CTRL+c] [CTRL+c] [CTRL+c]
[CTRL+c] [CTRL+\]
hello [CTRL+\] [Enter]
[CTRL+\] hello [Enter]
hel [CTRL+\] lo[Enter]

針對於上面各種情況,不同版本OS可能有不同的響應結果。

2.2. sigaction信號處理註冊

如果要想用程序控制上述各種情況的響應結果,就必須採用新的信號捕獲機制,即使用sigaction信號處理機

制。

函數原型:

#include 
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction也用於註冊一個信號處理函數。

參數signum爲需要捕捉的信號;

參數 act是一個結構體,裏面包含信號處理函數地址、處理方式等信息。

參數oldact是一個傳出參數,sigaction函數調用成功後,oldact裏面包含以前對signum的處理方式的信息。

如果函數調用成功,將返回0,否則返回-1

結構體 struct sigaction(注意名稱與函數sigaction相同)的原型爲:

struct sigaction {
void (*sa_handler)(int);   //老類型的信號處理函數指針
void (*sa_sigaction)(int, siginfo_t *, void *);//新類型的信號處理函數指針
sigset_t sa_mask;    //將要被阻塞的信號集合
int sa_flags;     //信號處理方式掩碼
void (*sa_restorer)(void); //保留,不要使用。
}

該結構體的各字段含義及使用方式:

1、字段sa_handler是一個函數指針,用於指向原型爲void handler(int)的信號處理函數地址, 即老類型 的信

號處理函數;

2、字段sa_sigaction也是一個函數指針,用於指向原型爲:

void handler(int iSignNum,siginfo_t *pSignInfo,void *pReserved);

的信號處理函數,即新類型的信號處理函數。

該函數的三個參數含義爲:

iSignNum:傳入的信號

pSignInfo:與該信號相關的一些信息,它是個結構體

pReserved:保留,現沒用

3、字段sa_handler和sa_sigaction只應該有一個生效,如果想採用老的信號處理機制,就應該讓sa_handler

指向正確的信號處 理函數;否則應該讓sa_sigaction指向正確的信號處理函數,並且讓字段sa_flags包含

SA_SIGINFO選項。

4、字段sa_mask是一個包含信號集合的結構體,該結構體內的信號表示在進行信號處理時,將要被阻塞的信

號。針對sigset_t結構體,有一組專門的函數對它進行處理,它們是:

        #include 
        int sigemptyset(sigset_t *set);      //清空信號集合set
        int sigfillset(sigset_t *set);      //將所有信號填充進setint sigaddset(sigset_t *set, int signum);    //往set中添加信號signum
        int sigdelset(sigset_t *set, int signum);    //從set中移除信號signum
        int sigismember(const sigset_t *set, int signum); //判斷signnum是不是包含在set

例如,如果打算在處理信號SIGINT時,只阻塞對SIGQUIT信號的處理,可以用如下種方法:

   struct sigaction act;
   sigemptyset(&act.sa_mask);
   sigaddset(&act_sa_mask,SIGQUIT);
   sigaction(SIGINT,&act,NULL);

字段sa_flags是一組掩碼的合成值,指示信號處理時所應該採取的一些行爲,各掩碼的含義爲:

掩碼 描述

SA_RESETHAND 處理完畢要捕捉的信號後,將自動撤消信號處理函數的註冊,即必須再重新註冊信號處理函

數,才能繼續處理接下來產生的信號。該選項不符合一般的信號處理流程,現已經被廢棄。

SA_NODEFER 在處理信號時,如果又發生了其它的信號,則立即進入其它信號的處理,等其它信號處理完畢

後,再繼續處理當前的信號,即遞規地處理。如果sa_flags包含了該掩碼,則結構體sigaction的sa_mask將無

效!

SA_RESTART 如果在發生信號時,程序正阻塞在某個系統調用,例如調用read()函數,則在處理完畢信號後,

接着從阻塞的系統返回。該掩碼符合普通的程序處理流程,所以一般來說,應該設置該掩碼,否則信號處理完

後,阻塞的系統調用將會返回失敗!

SA_SIGINFO 指示結構體的信號處理函數指針是哪個有效,如果sa_flags包含該掩碼,則sa_sigactiion指針有

效,否則是sa_handler指針有效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章