linux系統編程之信號(六):信號發送函數sigqueue和信號安裝函數sigaction

一,sigaction()

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

sigaction函數用於改變進程接收到特定信號後的行爲。該函數的第一個參數爲信號的值,能夠爲除SIGKILL及SIGSTOP外的任何一個特定有效的信號(爲這兩個信號定義本身的處理函數,將致使信號安裝錯誤)。第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對特定信號的處理,能夠爲空,進程會以缺省方式對信號處理;第三個參數oldact指向的對象用來保存原來對相應信號的處理,可指定oldact爲NULL。若是把第2、第三個參數都設爲NULL,那麼該函數可用於檢查信號的有效性。函數

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

sigaction結構定義以下:ui

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

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

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

二、由_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結構中的聯合數據成員確保該結構適應全部的信號,好比對於實時信號來講,則實際採用下面的結構形式:rest

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

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

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

由圖可知接收成功

相關文章
相關標籤/搜索