函數函數sigaction
1. 函數sigaction原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
分析:
- 參數 signum :要捕獲的信號。
- 參數act:truct sigaction 結構體,后面具體講解傳入參數,新的處理方式
- 參數oldact:返回舊的 struct sigaction 結構體,傳出參數,舊的處理方式
1.2 struct sigaction 結構體
1 struct sigaction 2 { 3 void(*sa_handler)(int); 4 void(*sa_sigaction)(int, siginfo_t *, void *); 5 sigset_t sa_mask; 6 int sa_flags; 7 void(*sa_restorer)(void); 8 };
分析:
- sa_handler : 不帶附加參數的信號處理函數指針
- sa_sigaction: 帶有附加參數的信號處理函數指針(兩個信號處理函數指針只能二選一)
- sa_mask: 在執行信號處理函數時,應該屏蔽掉哪些信號
- sa_flags: 用於控制信號行為,它的值可以是下面選項的組合。
- SA_SIGINFO:如果指定該選項,則向信號處理函數傳遞參數(這時應該使用 sa_sigaction 成員而不是 sa_handler).
- sa_restorer:該成員在早期是用來清理函數棧的,如今已被廢棄不用。
注意:sa_flags 的選項比較多,大部分可又自己做實驗驗證,有些是需要額外的知識,比如 SA_ONESTACK 和 SA_RESTART,這些放到后面講解。本節示例中,只需要把 sa_flags 設置為 0 即可。
信號捕捉特性:
- 進程正常運行時,默認PCB有一個信號屏蔽字,假定為☆,它決定了進程自動屏蔽哪些信號,當注冊了某個信號步捕捉函數,捕捉到該信號以后,要調用該函數,而該函數有可能執行很長時間,在這期間所屏蔽的信號不由☆指定,而由sa_mask來指定,調用完信號處理函數,再恢復為☆。
- xxx信號捕捉函數執行期間,XXX信號自動屏蔽。
- 阻塞的常規信號不支持排隊,產生多次只記錄一次(后32個實時信號支持排隊)
1. 測試代碼:
1 #include<stdio.h> 2 #include<signal.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 6 void docatch(int signo) 7 { 8 printf("%d signal is catch\n", signo); 9 } 10 11 int main() 12 { 13 int ret; 14 struct sigaction act; 15 act.sa_handler = docatch; 16 sigemptyset(&act.sa_mask); 17 sigaddset(&act.sa_mask, SIGQUIT); 18 act.sa_flags = 0; //默認屬性:信號捕捉函數執行期間,自動屏蔽本信號 19 ret = sigaction(SIGINT, &act, NULL); 20 if(ret < 0) 21 { 22 perror("sigaction error"); 23 exit(1); 24 } 25 while(1) 26 sleep(1); 27 return 0; 28 }
輸出結果:
2. 測試代碼:
1 #include<stdio.h> 2 #include<signal.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 6 void docatch(int signo) 7 { 8 printf("%d signal is catch\n", signo); 9 sleep(10); 10 printf("--------------finish-\n"); 11 } 12 13 int main() 14 { 15 int ret; 16 struct sigaction act; 17 act.sa_handler = docatch; 18 sigemptyset(&act.sa_mask); 19 sigaddset(&act.sa_mask, SIGQUIT); 20 act.sa_flags = 0; 21 ret = sigaction(SIGINT, &act, NULL); 22 if (ret < 0) 23 { 24 perror("sigaction error"); 25 exit(1); 26 } 27 while (1) 28 sleep(1); 29 return 0; 30 }
輸出結果:
3. 測試代碼:
1 #include <unistd.h> 2 #include <signal.h> 3 #include <stdio.h> 4 5 void printsigset(const sigset_t *set) 6 { 7 for (int i = 1; i <= 64; i++) 8 { 9 if (i == 33) putchar(' '); 10 if (sigismember(set, i) == 1) 11 putchar('1'); 12 else 13 putchar('0'); 14 } 15 puts(""); 16 } 17 18 void handler(int sig) 19 { 20 if (sig == SIGTSTP) 21 printf("hello SIGTSTP\n"); 22 if (sig == SIGINT) 23 printf("hello SIGINT\n"); 24 sleep(5); 25 sigset_t st; 26 sigpending(&st); 27 printsigset(&st); 28 } 29 30 int main() 31 { 32 printf("I'm %d\n", getpid()); 33 struct sigaction act, oldact; 34 act.sa_handler = handler; // 設置普通信號處理函數 35 sigemptyset(&act.sa_mask); // 向 sa_mask 中添加 SIGINT 36 sigaddset(&act.sa_mask, SIGINT); 37 act.sa_flags = 0; // 先置 0 38 39 sigaction(SIGTSTP, &act, &oldact); 40 sigaction(SIGINT, &act, &oldact); 41 42 while (1) 43 { 44 write(STDOUT_FILENO, ".", 1); 45 pause(); 46 } 47 return 0; 48 }
輸出結果:
分析:
- 當程序運行的時候,Ctrl C 進入 handler,然后立即 Ctrl Z 發現 handler 還未執行完就被 SIGTSTP 打斷.
- 當程序運行的時候,Ctrl Z 進入 handler,然后立即 Ctrl C 發現並不會被 SIGINT 打斷,這是因為該 handler 注冊的時候被設置了 SA_MASK = SIGINT。最后 handler 結束的時候打印了未決信號集,發現里頭有 SIGINT。所以 handler 結束后,又去繼續對 SIGINT 進行處理。