本文地址:http://www.cnblogs.com/archimedes/p/c-library-signal.html,轉載請注明源地址。
signal.h是C標准函數庫中的信號處理部分, 定義了程序執行時如何處理不同的信號。信號用作進程間通信, 報告異常行為(如除零)、用戶的一些按鍵組合(如同時按下Ctrl與C鍵,產生信號SIGINT)。信號是程序執行過程中發生的異常事件,同步信號的產生是因為程序自身的某些動作,例如除零或不正當的訪問存儲器,異步信號是由程序外部的行為引起的,比如有人敲擊了提示鍵或者另外一個程序(異步地執行)給你的程序發信號,都會引發一個異步信號。程序不能屏蔽的信號要求立即得到處理。如果不對發生的信號指定一種處理方法,那麼它就會被作為一種致命錯誤對待,然後程序就會以失敗的狀態終止執行
頭文件<singal.h>定義了一個無窮信號集的各種編碼值:
#define _NSIG 16 //能夠使用的信號的數量 #define SIGHUP 1 // 掛起 #define SIGINT 2 // 中斷 (DEL) #define SIGQUIT 3 // 退出 (ASCII FS) #define SIGILL 4 // 非法指令 #define SIGTRAP 5 // 跟蹤中斷 (捕捉的時候沒有重置) #define SIGABRT 6 // IOT 指令 #define SIGIOT 6 // 終止使用PDP-11 #define SIGUNUSED 7 // 備用代碼 #define SIGFPE 8 // 浮點型異常 #define SIGKILL 9 // kill (不能捕獲或忽略) #define SIGUSR1 10 // 用戶定義信號 # 1 #define SIGSEGV 11 // 內存段異常 #define SIGUSR2 12 // 用戶定義信號 # 2 #define SIGPIPE 13 // 在管道上寫操作沒有其他的讀操作 #define SIGALRM 14 // 警告 #define SIGTERM 15 // kill後獲得軟件終止信號 #define SIGEMT 7 // 舊式 #define SIGBUS 10 // 舊式 /* POSIX 要求定義下面的信號, 即使它們不被支持. 這裡給出這些定義, 但是它們不被支持. */ #define SIGCHLD 17 // 子進程終止或停止 #define SIGCONT 18 // 如果停止則繼續 #define SIGSTOP 19 // 停止信號 #define SIGTSTP 20 // 交互式停止信號 #define SIGTTIN 21 // 後台進程試圖讀 #define SIGTTOU 22 // 後台進程試圖寫
兩個函數:
raise函數,報告一個同步信號
int raise (int _sig);
signal函數,可以指定一種信號的處理方法
__sighandler_t signal (int _sig, __sighandler_t _func) ;
可以通過一下三種方式處理信號:
默認處理是終止程序,就像上面所描述的那樣
信號忽略是直接把它丟棄
信號處理是把控制權移給一個指定的函數
頭文件 <signal.h>為處理各種各樣的信號聲明了一個類型和兩個函數,並定義了幾個宏
定義的類型是:sig_atomic_t
typedef int sig_atomic_t;
該類型是整數類型,聲明為這種類型的對象可以作為一個原子實體被訪問,即使有異步中斷發生的時候也是如此。
定義的宏有: SIG_DFL、SIG_ERR、SIG_IGN
#define SIG_ERR ((__sighandler_t) -1) // 出錯返回值 #define SIG_DFL ((__sighandler_t) 0) // 默認信號處理 #define SIG_IGN ((__sighandler_t) 1) // 忽略信號
它們展開為具有不同值的常量表達式,這些表達式的類型和函數signal的第二個參數及返回值的類型兼容,並且它們的值與所有聲明的函數的地址都不相等。
指定信號處理
函數signal:
typedef void (*__sighandler_t) (int); __sighandler_t signal(int _sig, __sighandler_t _func);
或聲明成:
void (*signal(int sig, void (*func) (int)))(int);
signal函數通過3種方式來保證接收到的信號編號sig被依次處理,如果func的值是SIG_DFL,那麼會使用默認的信號處理方式;如果func的值是SIG_IGN,那麼這個信號就會被忽略;否則,func就指向一個函數,當這個信號發生時,就調用這個函數,這樣的函數就被稱為信號處理程序。
函數raise:
int raise(int sig);
函數raise把信號sig送給正在執行的程序
返回值:執行成功返回0,否則返回非0
從本質上說,信號處理是不可移植的。只有當必須指定一個已知的操作系統集的信號處理時,才能使用<signal.h>中聲明的函數,不用費盡心思把代碼變成通用的,這是不可能的
如果對一個信號的默認處理是可行的,那麼就選擇這種處理方法,使用自己的信號處理程序會降低可移植性並且可能導致程序對信號的誤處理增多,如果必須提供某個信號的處理程序,對其進行分類如下:
不可能返回值的信號處理程序,e.g:SIGFPE和SIGABRT
必須返回值的信號處理程序,e.g:SIGINT
不能返回值的信號處理程序最後會調用abort、exit或者longjmp來結束。當然,SIGABRT的處理程序不能調用abort來結束。處理程序不應該調用singal使自己重建。如果程序沒有終止,就把剩下的工作給其他的函數。如果信號是異步的,要注意程序所有的輸入輸出,因為這樣的操作也會中斷庫的執行。
必須返回的信號處理程序用一個return語句結束,如果它想讓自己復位,那麼應該在入口處立即返回。如果信號是異步的,就在一個sig_atomic_t類型的volatile數據對象中存儲一個非零值。不要執行其他任何對正在執行的程序有副作用的動作,例如輸入輸出和訪問其他的數據對象
下面是一個異步信號處理程序的例子:
#include<signal.h> static sig_atomic_t intflag = 0; static void field_int(int sig) { signal(SIGINT, &field_int); intflag = 1; return; }
這個程序調用signal(SIGINT, &field_int)來創建處理程序。它會不時地執行下面的代碼來檢查是否發生了異步交互式提示中斷:
if(intflag) { //執行中斷 intflag = 0; ... }
raise.c
#include<stdio.h> #include<signal.h> #include<stdlib.h> _sigfun *_sigtable[_NSIG] = {0}; int raise(int sig) { _sigfun *s; if(sig <= 0 || _NSIG <= sig) return -1; if((s = _sigtable[sig]) != SIG_IGN && s!= SIG_DFL) { _sigtable[sig] = SIG_DFL; (*s)(sig); } else if (s == SIG_DFL) { char ac[10], *p; switch(sig) { case SIGABRT: p = "abort"; break; case SIGFPE: p = "arithmetic error"; break; case SIGILL: p = "invalid executable code"; break; case SIGINT: p = "interruption"; break; case SIGSEGV: p = "invalid storage access"; break; case SIGTERM: p = "termination request"; break; default: *(p = &ac[sizeof(ac) - 1]) = '\0'; do *--p = sig % 10 + '0'; while((sig /= 10) != 0); fputs("signal #", stderr); break; } fputs(p, strerr); fputs(" -- terminating\n", stderr); exit(EXIT_FAILURE); } return 0; }
signal.c
#include<signal.h> _sigfun *_sigtable[_NSIG]; _sigfun *signal(int sig, _sigfun *fun) { _sigfun *s; if(sig <= 0 || _NSIG <= sig || fun == SIG_ERR) return SIG_ERR; s = _sigtable[sig]; _sigtable[sig] = fun; return s; }