1.什么是信號
信號是Linux系統響應某些條件而產生的一個事件,接收到該信號的進程會執行相應的操作。
2.信號的產生
1)由硬件產生,如從鍵盤輸入Ctrl+C可以終止當前進程
2)由其他進程發送,如可在shell進程下,使用命令 kill -信號標號 PID,向指定進程發送信號。
3)異常,進程異常時會發送信號
3.信號的處理
信號是由操作系統來處理的,說明信號的處理在內核態。
信號不一定會立即被處理,此時會儲存在信號的信號表中。
處理過程示意圖:
由上圖中可看出信號有三種處理方式:
1)忽略
2)默認處理方式:操作系統設定的默認處理方式
3)自定義信號處理方式:可自定義信號處理函數
4.自定義信號處理方式
1)signal函數
原型:
void (*signal(int sig, void (*func)(int)))(int);
sig:信號值
func:信號處理函數指針,參數為信號值
代碼示例如下:
#include <signal.h> #include <stdio.h> void ouch(int sig) { printf("\nOUCH! - I got signal %d\n", sig); //恢復終端中斷信號SIGINT的默認行為 (void) signal(SIGINT, SIG_DFL); } int main() { //改變終端中斷信號SIGINT的默認行為,使之執行ouch函數 //而不是終止程序的執行 (void) signal(SIGINT, ouch); while(1) { printf("Hello World!\n"); sleep(1); } return 0; }
輸出結果:
2)sigaction函數
原型:
int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);
sig:信號值
act:指定信號的動作
oact:保存原信號的動作
sigaction結構體的定義如下:
void (*)(int) sa_handler;處理函數指針,相當於signal函數的func參數。
sigset_t sa_mask;處理過程中,屏蔽對sa_mask信號集的處理,sa_mask可以消除信號間的競態。
int sa_flags;信號處理修改器:處理函數執行完后,信號處理方式修改。如SA_RESETHAND,將信號處理方式重置為SIG_DFL
代碼示例如下:
#include <stdio.h> #include <signal.h> void ouch(int sig) { printf("\nOUCH! - I got signal %d\n", sig); } int main() { struct sigaction act; act.sa_handler = ouch; //創建空的信號屏蔽字,即不屏蔽任何信息 sigemptyset(&act.sa_mask); //使sigaction函數重置為默認行為 act.sa_flags = SA_RESETHAND; sigaction(SIGINT, &act, 0); while(1) { printf("Hello World!\n"); sleep(1); } return 0; }
輸出結果:
4.信號的發送
1)kill函數
int kill(pid_t pid,int signo);
pid:進程ID
signo:信號值
2)raise函數:只能向當前進程發信號
int raise(int signo);
signo:信號值
3)abort函數:發送SIGABRT信號,可以讓進程異常終止
void abort(void);
4)alarm函數:發送SIGALRM鬧鍾信號
unsigned int alarm(unsigned int seconds);
5.信號的阻塞
阻塞是阻止進程收到該信號,此時信號處於未決狀態,放入進程的未決信號表中,
當解除對該信號的阻塞時,未決信號會被進程接收。
1)阻塞信號
原型:
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
how:設置block阻塞表的方式
a.SIG_BLOCK:將信號集添加到block表中
b.SIG_UNBLOCK:將信號集從block表中刪除
c.SIG_SETMASK:將信號集設置為block表
set:要設置的集合
oset:設置前保存之前block表信息
2)獲取未決信號
前面已經講過,阻塞的信號處於未決的狀態,會放入進程的未決信號表。
原型:
int sigpending(sigset_t *set);
set:out型參數,會將獲得的當前進程的pending未決表中的信號集傳入。
代碼示例如下:
#include <stdio.h> #include <sys/signal.h> #include <sys/types.h> #include <signal.h> void func(int num) { printf("catch signal number is %d",num); } void printfpendingsignal(sigset_t *set) { int i; for(i=1;i<32;++i) { if(sigismember(set,i)) { printf("1"); } else { printf("0"); } } printf("\n"); } int main() { sigset_t s,p,o; signal(SIGINT,func); sigemptyset(&s); sigemptyset(&p); sigemptyset(&o); sigaddset(&s,SIGINT); sigprocmask(SIG_SETMASK,&s,&o); int count=0; while(1) { sigpending(&p); printfpendingsignal(&p); sleep(1); if(count++==10) { printf("recover!\n"); sigprocmask(SIG_SETMASK,&o,NULL); } } return 0; }
輸出結果:
6.信號處理函數的安全問題
如果信號處理過程中被中斷,再次調用,然后返回到第一次調用時,要保證操作的正確性。
這就要求信號處理函數必須是可重入的。
可重入函數表如下:
7.一些常見的信號
如果進程接收到上面的這些信號,又沒有安排捕獲它,進程就會終止。
其他的一些信號如下: