使用 sigaction 函數:
signal 函數的使用方法簡單,但並不屬於 POSIX 標准,在各類 UNIX 平台上的實現不盡相同,因此其用途受
到了一定的限制。而 POSIX 標准定義的信號處理接口是 sigaction 函數,其接口頭文件及原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
◆ signum:要操作的信號。
◆ act:要設置的對信號的新處理方式。
◆ oldact:原來對信號的處理方式。
◆ 返回值:0 表示成功,-1 表示有錯誤發生。
struct 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);
};
在這個結構體中,成員 sa_handler 是一個函數指針,其含義與 signal 函數中的信號處理函數類似。成員
sa_sigaction 則是另一個信號處理函數,它有三個參數,可以獲得關於信號的更詳細的信息。當 sa_flags 成員的值
包含了 SA_SIGINFO 標志時,系統將使用 sa_sigaction 函數作為信號處理函數,否則使用 sa_handler 作為信號處理
函數。在某些系統中,成員 sa_handler 與 sa_sigaction 被放在聯合體中,因此使用時不要同時設置。
sa_mask 成員用來指定在信號處理函數執行期間需要被屏蔽的信號,特別是當某個信號被處理時,它自身會被
自動放入進程的信號掩碼,因此在信號處理函數執行期間這個信號不會再度發生。
sa_flags 成員用於指定信號處理的行為,它可以是一下值的“按位或”組合。
◆ SA_RESTART:使被信號打斷的系統調用自動重新發起。
◆ SA_NOCLDSTOP:使父進程在它的子進程暫停或繼續運行時不會收到 SIGCHLD 信號。
◆ SA_NOCLDWAIT:使父進程在它的子進程退出時不會收到 SIGCHLD 信號,這時子進程如果退出也不會成為僵
屍進程。
◆ SA_NODEFER:使對信號的屏蔽無效,即在信號處理函數執行期間仍能發出這個信號。
◆ SA_RESETHAND:信號處理之后重新設置為默認的處理方式。
◆ SA_SIGINFO:使用 sa_sigaction 成員而不是 sa_handler 作為信號處理函數。
re_restorer 成員則是一個已經廢棄的數據域,不要使用。
下面用一個例程來說明 sigaction 函數的使用,代碼如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <signal.h> 4 #include <errno.h> 5 6 static void sig_usr(int signum) 7 { 8 if(signum == SIGUSR1) 9 { 10 printf("SIGUSR1 received\n"); 11 } 12 else if(signum == SIGUSR2) 13 { 14 printf("SIGUSR2 received\n"); 15 } 16 else 17 { 18 printf("signal %d received\n", signum); 19 } 20 } 21 22 int main(void) 23 { 24 char buf[512]; 25 int n; 26 struct sigaction sa_usr; 27 sa_usr.sa_flags = 0; 28 sa_usr.sa_handler = sig_usr; //信號處理函數 29 30 sigaction(SIGUSR1, &sa_usr, NULL); 31 sigaction(SIGUSR2, &sa_usr, NULL); 32 33 printf("My PID is %d\n", getpid()); 34 35 while(1) 36 { 37 if((n = read(STDIN_FILENO, buf, 511)) == -1) 38 { 39 if(errno == EINTR) 40 { 41 printf("read is interrupted by signal\n"); 42 } 43 } 44 else 45 { 46 buf[n] = '\0'; 47 printf("%d bytes read: %s\n", n, buf); 48 } 49 } 50 51 return 0; 52 }
在這個例程中使用 sigaction 函數為 SIGUSR1 和 SIGUSR2 信號注冊了處理函數,然后從標准輸入讀入字符
。程序運行后首先輸出自己的 PID,如:
My PID is 5904
這時如果從另外一個終端向進程發送 SIGUSR1 或 SIGUSR2 信號,用類似如下的命令:
kill -USR1 5904
則程序將繼續輸出如下內容:
SIGUSR1 received
read is interrupted by signal
這說明用 sigaction 注冊信號處理函數時,不會自動重新發起被信號打斷的系統調用。如果需要自動重新發
起,則要設置 SA_RESTART 標志,比如在上述例程中可以進行類似一下的設置:
sa_usr.sa_flags = SA_RESTART;