信號發送函數sigqueue和信號安裝函數sigaction

一,sigaction()

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact));數據結構

sigaction 函數用於改變進程接收到特定信號後的行爲。該函數的函數

第一個參數爲信號的值,能夠爲除SIGKILLSIGSTOP外的任何一個特定有效的信號(爲這兩個 信號定義本身的處理函數,將致使信號安裝錯誤)。學習

第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對 特定信號的處理,能夠爲空,進程會以缺省方式對信號處理;ui

第三個參數oldact指向的對象用來保存原來對相應信號的處理,可指定oldact爲 NULL。若是把第2、第三個參數都設爲NULL,那麼該函數可用於檢查信號的有效性。spa

第二個參數最爲重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數執行過程當中應屏蔽掉哪些函數等等。指針

sigaction結構定義以下:rest

struct sigaction
{         
    union
    {
         __sighandler_t _sa_handler;           
        
void (*_sa_sigaction)(int,struct siginfo *, void *);
    }_u                            
    sigset_t sa_mask;                   
    unsigned
long sa_flags;                  
    
void (*sa_restorer)(void);                 
}
code

其中,sa_restorer,已過期,POSIX不支持它,不該再被使用。對象

進程

struct sigaction
{

   
void (*_sa_handler)(int);
    
void (*_sa_sigaction)(int,struct siginfo *, void *);
    sigset_t sa_mask;
    unsigned
long sa_flags;
}

一、聯合數據結構中的兩個元素_sa_handler以及*_sa_sigaction指定信號關聯函數,即用戶指定的信號處理函數。除了能夠是用戶自定義的處理函數外,還能夠爲SIG_DFL(採用缺省的處理方式),也能夠爲SIG_IGN(忽略信號)。

二、 由_sa_handler指定的處理函數只有一個參數,即信號值,因此信號不能傳遞除信號值以外的任何信息;

_sa_sigaction是指定的信號處 理函數帶有三個參數,是爲實時信號而設的(固然一樣支持非實時信號),它指定一個3參數信號處理函數。第一個參數爲信號值,第三個參數沒有使用 (posix沒有規範使用該參數的標準),第二個參數是指向siginfo_t結構的指針,結構中包含信號攜帶的數據值,參數所指向的結構以下:

typedef struct siginfo_t
{

    int si_signo;    //信號編號
    
int si_errno;    //若是爲非零值則錯誤代碼與之關聯
    int si_code;    //說明進程如何接收信號以及從何處收到


    pid_t si_pid;    //適用於SIGCHLD,表明被終止進程的PID
    pid_t si_uid;    //適用於SIGCHLD,表明被終止進程所擁有進程的UID
    int si_status;    //適用於SIGCHLD,表明被終止進程的狀態
    clock_t si_utime;    //適用於SIGCHLD,表明被終止進程所消耗的用戶時間
    clock_t si_stime;    //適用於SIGCHLD,表明被終止進程所消耗系統的時間
    sigval_t si_value;
    int si_int;
    
void * si_ptr;
    void* si_addr;
    int si_band;
    int si_fd;
};

siginfo_t結構中的聯合數據成員確保該結構適應全部的信號,好比對於實時信號來講,則實際採用下面的結構形式:

typedef struct
{       
    int si_signo;       
    int si_errno;                   
    
int si_code;                   
    union sigval si_value;           
} siginfo

結構的第四個域一樣爲一個聯合數據結構:

union sigval
{       
    
int sival_int;               
    
void *sival_ptr;           
}

採 用聯合數據結構,說明siginfo_t結構中的si_value要麼持有一個4字節的整數值,要麼持有一個指針,這就構成了與信號相關的數據。在信號的 處理函數中,包含這樣的信號相關數據指針,但沒有規定具體如何對這些數據進行操做,操做方法應該由程序開發人員根據具體任務事先約定。

sigval 結構體:系統調用sigqueue發送信號時,sigqueue的第三個參數就是sigval聯合數據結構,當調用sigqueue時,該數據結構中的數 據就將拷貝到信號處理函數的第二個參數中。這樣,在發送信號同時,就可讓信號傳遞一些附加信息。信號能夠傳遞信息對程序開發是很是有意義的。

siginfo_t.si_value與sigqueue(pid_t pid, int sig, const union sigval val)第三個參數關聯即:

因此經過siginfo_t.si_value能夠得到sigqueue(pid_t pid, int sig, const union sigval val)第三個參數傳遞過來的數據。

如:siginfo_t.si_value.sival_int或siginfo_t.si_value.sival_ptr

其實siginfo_t.si_int直接與sigval.sival_int關聯

siginfo_t.si_ptr直接與sigval.sival_ptr關聯,因此也可同這種方式得到sigqueue發送過來的數據。

 

信號參數的傳遞過程可圖示以下:

三、sa_mask指定在信號處理程序執行過程當中,哪些信號應當被阻塞。缺省狀況下當前信號自己被阻塞,防止信號的嵌套發送,除非指定SA_NODEFER或者SA_NOMASK標誌位,處理程序執行完後,被阻塞的信號開始執行。

注:請注意sa_mask指定的信號阻塞的前提條件,是在由sigaction()安裝信號的處理函數執行過程當中由sa_mask指定的信號才被阻塞。

四、 sa_flags中包含了許多標誌位,包括剛剛提到的SA_NODEFER及SA_NOMASK標誌位。另外一個比較重要的標誌位是SA_SIGINFO, 當設定了該標誌位時,表示信號附帶的參數能夠被傳遞到信號處理函數中,所以,應該爲sigaction結構中的sa_sigaction指定處理函數,而 不該該爲sa_handler指定信號處理函數,不然,設置該標誌變得毫無心義。即便爲sa_sigaction指定了信號處理函數,若是不設置 SA_SIGINFO,信號處理函數一樣不能獲得信號傳遞過來的數據,在信號處理函數中對這些信息的訪問都將致使段錯誤(Segmentation fault)。

注:不少文獻在闡述該標誌位時都認爲,若是設置了該標誌位,就必須定義三參數信號處理函數。實際不是這樣的,驗證方法很簡 單:本身實現一個單一參數信號處理函數,並在程序中設置該標誌位,能夠察看程序的運行結果。實際上,能夠把該標誌位當作信號是否傳遞參數的開關,若是設置 該位,則傳遞參數;不然,不傳遞參數。

二,sigqueue()

以前學過kill,raise,alarm,abort等功能稍簡單的信號發送函數,如今咱們學習一種新的功能比較強大的信號發送函數sigqueue.

#include <sys/types.h>

#include <signal.h>

int sigqueue(pid_t pid, int sig, const union sigval val)

調用成功返回 0;不然,返回 -1。

sigqueue()是比較新的發送信號系統調用,主要是針對實時信號提出的(固然也支持前32種),支持信號帶有參數,與函數sigaction()配合使用。

sigqueue的第一個參數是指定接收信號的進程ID,第二個參數肯定即將發送的信號,第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即一般所說的4字節值。

typedef union sigval {

               int  sival_int;

               void *sival_ptr;

}sigval_t;

sigqueue() 比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。若是signo=0,將會執行錯誤檢 查,但實際上不發送任何信號,0值信號可用於檢查pid的有效性以及當前進程是否有權限向目標進程發送信號。

在調用sigqueue 時,sigval_t指定的信息會拷貝到對應sig 註冊的3參數信號處理函數的siginfo_t結構中,這樣信號處理函數就能夠處理這些信息了。因爲sigqueue系統調用支持發送帶參數信號,因此比 kill()系統調用的功能要靈活和強大得多。

三,sigqueue與sigaction應用實例

實例一:利用sigaction安裝SIGINT信號

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) /    
    do /    
    { /        
        perror(m); /        
        exit(EXIT_FAILURE); /    
    } while(0)
void handler(int sig);
int main(int argc, char *argv[])
{    
    struct sigaction act;    
    act.sa_handler = handler;    
    sigemptyset(&act.sa_mask);    
    act.sa_flags = 0;    
    //由於不關心SIGINT上一次的struct sigaction因此,oact爲NULL    
    //與signal(handler,SIGINT)相同    
    if (sigaction(SIGINT, &act, NULL) < 0)        
        ERR_EXIT("sigaction error/n");    
    for (;;)        
        pause();    
    return 0;
}
void handler(int sig)
{    
    printf("recv a sig=%d/n", sig);
}

結果:

QQ截圖20130715165301

實例二:利用sigaction實現signal,實際上signal底層實現就是利用sigaction

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) /    
    do /    
    { /        
        perror(m); /        
        exit(EXIT_FAILURE); /    
    } while(0)
void handler(int sig);
__sighandler_t my_signal(int sig, __sighandler_t handler);
int main(int argc, char *argv[])
{   
    my_signal(SIGINT, handler);    
    for (;;)        
        pause();    
    return 0;
}
__sighandler_t my_signal(int sig, __sighandler_t handler)
{    
    struct sigaction act;    
    struct sigaction oldact;    
    act.sa_handler = handler;    
    sigemptyset(&act.sa_mask);    
    act.sa_flags = 0;    
    if (sigaction(sig, &act, &oldact) < 0)        
        return SIG_ERR;    
    return oldact.sa_handler;
}
void handler(int sig)
{    
    printf("recv a sig=%d/n", sig);
}

結果:

QQ截圖20130715165842

可知my_signal與系統調用signal具備相同的效果

實例三:驗證sigaction.sa_mask效果

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) /    
    do /    
    { /        
        perror(m); /        
        exit(EXIT_FAILURE); /    
    } while(0)
void handler(int sig);
int main(int argc, char *argv[])
{    
    struct sigaction act;    
    act.sa_handler = handler;    
    sigemptyset(&act.sa_mask);    
    sigaddset(&act.sa_mask, SIGQUIT);    
    act.sa_flags = 0;    
    if (sigaction(SIGINT, &act, NULL) < 0)        
        ERR_EXIT("sigaction error");    
    struct sigaction act2;    
    act2.sa_handler = handler;    
    sigemptyset(&act2.sa_mask);    
    act2.sa_flags = 0;    
    if (sigaction(SIGQUIT, &act2, NULL) < 0)       
        ERR_EXIT("sigaction error");    
    for (;;)        
        pause();    
    return 0;
}
void handler(int sig)
{    
    if(sig == SIGINT)
    {        
        printf("recv a SIGINT signal/n");        
        sleep(5);    
    }    
    if (sig == SIGQUIT)    
    {        
        printf("recv a SIGQUIT signal/n");    
    }
}

結果:

QQ截圖20130715170929

可 知,安裝信號SIGINT時,將SIGQUIT加入到sa_mask阻塞集中,則當SIGINT信號正在執行處理函數時,SIGQUIT信號將被阻塞,只 有當SIGINT信號處理函數執行完後才解除對SIGQUIT信號的阻塞,因爲SIGQUIT是不可靠信號,不支持排隊,因此只遞達一次

示例四:給自身發送int型數據

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sighandler(int signo, siginfo_t *info,void *ctx);//給自身傳遞信息
int main(void)
{    
    struct sigaction act;    
    act.sa_sigaction = sighandler;    
    sigemptyset(&act.sa_mask);    
    act.sa_flags = SA_SIGINFO;//信息傳遞開關    
    if(sigaction(SIGINT,&act,NULL) == -1)
    {        
        perror("sigaction error");        
        exit(EXIT_FAILURE);    
    }    
    sleep(2);    
    union sigval mysigval;   
      mysigval.sival_int = 100;    
    if(sigqueue(getpid(),SIGINT,mysigval) == -1)
    {        
        perror("sigqueue error");        
        exit(EXIT_FAILURE);    
    }    
    return 0;
}
void sighandler(int signo, siginfo_t *info,void *ctx)
{    
    //如下兩種方式都能得到sigqueue發來的數據    
    printf("receive the data from siqueue by info->si_int is %d/n",info->si_int);
       printf("receive the data from siqueue by info->si_value.sival_int is 
                            %d/n",info->si_value.sival_int);
}

結果:

QQ截圖20130715183155

示例五:進程間傳遞數據

接收端:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sighandler(int signo, siginfo_t *info,void *ctx);//給自身傳遞信息
int main(void)
{    
    struct sigaction act;    
    act.sa_sigaction = sighandler;    
    sigemptyset(&act.sa_mask);    
    act.sa_flags = SA_SIGINFO;//信息傳遞開關    
    if(sigaction(SIGINT,&act,NULL) == -1)
    {        
    perror("sigaction error");        
    exit(EXIT_FAILURE);    
    }    
    for(; ;)
    {        
        printf("waiting a SIGINT signal..../n");        
        pause();    
    }    
    return 0;
}
void sighandler(int signo, siginfo_t *info,void *ctx)
{    
    //如下兩種方式都能得到sigqueue發來的數據    
    printf("receive the data from siqueue by info->si_int is %d/n",info->si_int);
       printf("receive the data from siqueue by info->si_value.sival_int is 
                            %d/n",info->si_value.sival_int);
}

發送端:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char **argv)
{    
    if(argc != 2)
    {        
        fprintf(stderr,"usage:%s pid/n",argv[0]);        
        exit(EXIT_FAILURE);    
    }    
    pid_t pid = atoi(argv[1]);        
    sleep(2);    
    union sigval mysigval;    
    mysigval.sival_int = 100;    
    printf("sending SIGINT signal to %d....../n",pid);    
    if(sigqueue(pid,SIGINT,mysigval) == -1)
    {        
        perror("sigqueue error");        
        exit(EXIT_FAILURE);    
    }    
    return 0;
}

結果:

QQ截圖20130715191546

相關文章
相關標籤/搜索