C/C++實現捕獲所有信號的示例詳解
一、原理
Linux的信號可能在你無法意識到的情況下發(fā)生。
比如socket網絡斷開,默認情況下發(fā)送SIGPIPE給正在send/recv的進程從而殺死進程,忘記了處理這個就很麻煩,程序大部分時間很正常,偶爾奇怪地終止,可能要花很大力氣才會找到原因。
而Linux的信號是不帶任何數據的,知道一個連接斷開又能怎么樣呢?又不知道是哪個連接斷開(其實我們會根據send/recv的返回值處理的嘛)。
所以為了省力,我們可以在程序開始處捕獲所有信號,然后再根據實際運行時的情況針對性處理(大部分情況根本不需要任何處理)。
關于信號的更多知識在后面的“信號描述”函數里。
二、基礎
處理信號最簡單的方式就是調用signal函數,這個函數設置信號處理方法并返回之前的信號處理方法(以便你可以在執(zhí)行自己的處理之前或之后調用原來的處理方法從而形成調用鏈)。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal還有幾種形式,不過這種最簡單了。
三、代碼
捕獲全部信號的代碼
int __all_sig_catch(int argc, char ** argv, int fun(int, char **))
{
signal(SIGABRT, sig_default);
signal(SIGALRM, sig_default);
signal(SIGBUS, sig_default);
signal(SIGCHLD, sig_default);
signal(SIGCONT, sig_default);
signal(SIGFPE, sig_default);
signal(SIGHUP, sig_default);
signal(SIGILL, sig_default);
//signal(SIGINT, sig_default);//ctrl-c
signal(SIGIO, sig_default);
signal(SIGIOT, sig_default);
signal(SIGKILL, sig_default);
signal(SIGPIPE, sig_default);
signal(SIGPOLL, sig_default);
signal(SIGPROF, sig_default);
signal(SIGPWR, sig_default);
signal(SIGQUIT, sig_default);
signal(SIGSEGV, sig_default);
signal(SIGSTOP, sig_default);
signal(SIGSYS, sig_default);
signal(SIGTERM, sig_default);
signal(SIGTRAP, sig_default);
signal(SIGTSTP, sig_default);
signal(SIGTTIN, sig_default);
signal(SIGTTOU, sig_default);
signal(SIGURG, sig_default);
signal(SIGUSR1, sig_default);
signal(SIGUSR2, sig_default);
signal(SIGVTALRM, sig_default);
signal(SIGWINCH, sig_default);
signal(SIGXCPU, sig_default);
signal(SIGXFSZ, sig_default);
int ret;
try
{
ret = fun(argc, argv);
}
catch (...)
{
thelog << "未處理的異常發(fā)生" << ende;
return __LINE__;
}
return ret;
}
這個函數的調用方式是給main函數套一層殼,當然你也可以把參數都去掉,只保留設置信號處理函數的那部分。
SIGINT沒有捕獲,這是ctrl-c產生的信號,我確實需要這樣結束程序。
最后還捕獲了一下未處理的異常,這也是經常疏忽的部分。如果需要生成core文件,可以調用abort()。
注意SIGKILL和SIGSTOP是無法捕獲的(雖然上面代碼里面有)。
調用代碼
int _main(int argc, char ** argv)
{
......
}
int main(int argc, char ** argv)
{
return __all_sig_catch(argc, argv, _main);
}
信號處理函數
sig_default是設置的信號處理函數,必須符合signal函數的要求:
extern "C" void sig_default(int sig)
{
signal(sig, sig_default);
cout << "pid=" << getpid() << " " << sigstr(sig) << endl;
}
注意必須是extern "C",代碼里再次調用了signal設置信號處理函數,這么做的原因我現在不是很確定,不過這么做起碼應該不會有什么問題。
嚴格說應該調用一下之前的處理函數的,不過這樣就會很復雜。因為我們在main函數開始處設置,所有信號應該是還沒有被設置過的。
信號描述
sigstr是輸出信號名稱的函數:
//信號的描述
char const * sigstr(long sig)
{
switch(sig)
{
case SIGABRT : return "SIGABRT 進程調用abort函數,進程非正常退出";
case SIGALRM : return "SIGALRM 用alarm函數設置的timer超時或setitimer函數設置的interval timer超時";
case SIGBUS : return "SIGBUS 某種特定的硬件異常,通常由內存訪問引起";
case SIGCHLD : return "SIGCHLD 子進程Terminate或Stop";
case SIGCONT : return "SIGCONT 從stop中恢復運行";
#ifndef _LINUXOS
case SIGEMT : return "SIGEMT 和實現相關的硬件異常";
#endif
case SIGFPE : return "SIGFPE 數學相關的異常,如被0除,浮點溢出,等等";
case SIGHUP : return "SIGHUP 終端斷開";
case SIGILL : return "SIGILL 非法指令異常";
//case SIGINFO : return "SIGINFO BSD signal。由Status Key產生,通常是CTRL+T。發(fā)送給所有Foreground Group的進程 ";
case SIGINT : return "SIGINT 由Interrupt Key產生,通常是CTRL+C或者DELETE";
case SIGIO : return "SIGIO 異步IO事件";
//case SIGIOT : return "SIGIOT 實現相關的硬件異常,一般對應SIGABRT ";
case SIGKILL : return "SIGKILL 強制中止";
case SIGPIPE : return "SIGPIPE 在reader中止之后寫Pipe的時候發(fā)送";
//case SIGPOLL : return "SIGPOLL 當某個事件發(fā)送給Pollable Device的時候發(fā)送 ";
case SIGPROF : return "SIGPROF Setitimer指定的Profiling Interval Timer所產生";
case SIGPWR : return "SIGPWR 和系統(tǒng)相關。和UPS相關。";
case SIGQUIT : return "SIGQUIT 輸入Quit Key(CTRL+\\)";
case SIGSEGV : return "SIGSEGV 非法內存訪問";
case SIGSTOP : return "SIGSTOP 中止進程";
case SIGSYS : return "SIGSYS 非法系統(tǒng)調用";
case SIGTERM : return "SIGTERM 請求中止進程,kill命令缺省發(fā)送";
case SIGTRAP : return "SIGTRAP 實現相關的硬件異常。一般是調試異常";
case SIGTSTP : return "SIGTSTP Suspend Key,一般是Ctrl+Z";
case SIGTTIN : return "SIGTTIN 當Background Group的進程嘗試讀取Terminal的時候發(fā)送";
case SIGTTOU : return "SIGTTOU 當Background Group的進程嘗試寫Terminal的時候發(fā)送";
case SIGURG : return "SIGURG 當out-of-band data接收的時候可能發(fā)送";
case SIGUSR1 : return "SIGUSR1 用戶自定義signal 1";
case SIGUSR2 : return "SIGUSR2 用戶自定義signal 2";
case SIGVTALRM : return "SIGVTALRM setitimer函數設置的Virtual Interval Timer超時的時候";
case SIGWINCH : return "SIGWINCH 當Terminal的窗口大小改變的時候,發(fā)送給Foreground Group的所有進程";
case SIGXCPU : return "SIGXCPU 當CPU時間限制超時的時候";
case SIGXFSZ : return "SIGXFSZ 進程超過文件大小限制";
default: return "未知的信號";
}
}
其中屏蔽掉了一些是因為兼容性問題(這段代碼以前要在IBM、SUN、HP的小型機上運行,后來才改在Linux上運行),你可以根據需要添加。
到此這篇關于C/C++實現捕獲所有信號的示例詳解的文章就介紹到這了,更多相關C++捕獲所有信號內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C語言中字符和字符串處理(ANSI字符和Unicode字符)
這篇文章主要介紹了C語言與C++中字符和字符串處理(ANSI字符和Unicode字符)的詳細內容,非常的全面,這里推薦給大家,希望大家能夠喜歡。2015-03-03

