#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)。
注:不少文獻在闡述該標誌位時都認爲,若是設置了該標誌位,就必須定義三參數信號處理函數。實際不是這樣的,驗證方法很簡單:本身實現一個單一參數信號處理函數,並在程序中設置該標誌位,能夠察看程序的運行結果。實際上,能夠把該標誌位當作信號是否傳遞參數的開關,若是設置該位,則傳遞參數;不然,不傳遞參數。
以前學過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()系統調用的功能要靈活和強大得多。
實例一:利用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); }
結果:
實例二:利用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); }
結果:
可知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"); } }
結果:
可知,安裝信號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); }
結果:
示例五:進程間傳遞數據
接收端:
#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; }
結果:
由圖可知接收成功