C/C++實(shí)現(xiàn)捕獲所有信號(hào)的示例詳解
一、原理
Linux的信號(hào)可能在你無(wú)法意識(shí)到的情況下發(fā)生。
比如socket網(wǎng)絡(luò)斷開(kāi),默認(rèn)情況下發(fā)送SIGPIPE給正在send/recv的進(jìn)程從而殺死進(jìn)程,忘記了處理這個(gè)就很麻煩,程序大部分時(shí)間很正常,偶爾奇怪地終止,可能要花很大力氣才會(huì)找到原因。
而Linux的信號(hào)是不帶任何數(shù)據(jù)的,知道一個(gè)連接斷開(kāi)又能怎么樣呢?又不知道是哪個(gè)連接斷開(kāi)(其實(shí)我們會(huì)根據(jù)send/recv的返回值處理的嘛)。
所以為了省力,我們可以在程序開(kāi)始處捕獲所有信號(hào),然后再根據(jù)實(shí)際運(yùn)行時(shí)的情況針對(duì)性處理(大部分情況根本不需要任何處理)。
關(guān)于信號(hào)的更多知識(shí)在后面的“信號(hào)描述”函數(shù)里。
二、基礎(chǔ)
處理信號(hào)最簡(jiǎn)單的方式就是調(diào)用signal函數(shù),這個(gè)函數(shù)設(shè)置信號(hào)處理方法并返回之前的信號(hào)處理方法(以便你可以在執(zhí)行自己的處理之前或之后調(diào)用原來(lái)的處理方法從而形成調(diào)用鏈)。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal還有幾種形式,不過(guò)這種最簡(jiǎn)單了。
三、代碼
捕獲全部信號(hào)的代碼
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;
}
這個(gè)函數(shù)的調(diào)用方式是給main函數(shù)套一層殼,當(dāng)然你也可以把參數(shù)都去掉,只保留設(shè)置信號(hào)處理函數(shù)的那部分。
SIGINT沒(méi)有捕獲,這是ctrl-c產(chǎn)生的信號(hào),我確實(shí)需要這樣結(jié)束程序。
最后還捕獲了一下未處理的異常,這也是經(jīng)常疏忽的部分。如果需要生成core文件,可以調(diào)用abort()。
注意SIGKILL和SIGSTOP是無(wú)法捕獲的(雖然上面代碼里面有)。
調(diào)用代碼
int _main(int argc, char ** argv)
{
......
}
int main(int argc, char ** argv)
{
return __all_sig_catch(argc, argv, _main);
}
信號(hào)處理函數(shù)
sig_default是設(shè)置的信號(hào)處理函數(shù),必須符合signal函數(shù)的要求:
extern "C" void sig_default(int sig)
{
signal(sig, sig_default);
cout << "pid=" << getpid() << " " << sigstr(sig) << endl;
}
注意必須是extern "C",代碼里再次調(diào)用了signal設(shè)置信號(hào)處理函數(shù),這么做的原因我現(xiàn)在不是很確定,不過(guò)這么做起碼應(yīng)該不會(huì)有什么問(wèn)題。
嚴(yán)格說(shuō)應(yīng)該調(diào)用一下之前的處理函數(shù)的,不過(guò)這樣就會(huì)很復(fù)雜。因?yàn)槲覀冊(cè)趍ain函數(shù)開(kāi)始處設(shè)置,所有信號(hào)應(yīng)該是還沒(méi)有被設(shè)置過(guò)的。
信號(hào)描述
sigstr是輸出信號(hào)名稱的函數(shù):
//信號(hào)的描述
char const * sigstr(long sig)
{
switch(sig)
{
case SIGABRT : return "SIGABRT 進(jìn)程調(diào)用abort函數(shù),進(jìn)程非正常退出";
case SIGALRM : return "SIGALRM 用alarm函數(shù)設(shè)置的timer超時(shí)或setitimer函數(shù)設(shè)置的interval timer超時(shí)";
case SIGBUS : return "SIGBUS 某種特定的硬件異常,通常由內(nèi)存訪問(wèn)引起";
case SIGCHLD : return "SIGCHLD 子進(jìn)程Terminate或Stop";
case SIGCONT : return "SIGCONT 從stop中恢復(fù)運(yùn)行";
#ifndef _LINUXOS
case SIGEMT : return "SIGEMT 和實(shí)現(xiàn)相關(guān)的硬件異常";
#endif
case SIGFPE : return "SIGFPE 數(shù)學(xué)相關(guān)的異常,如被0除,浮點(diǎn)溢出,等等";
case SIGHUP : return "SIGHUP 終端斷開(kāi)";
case SIGILL : return "SIGILL 非法指令異常";
//case SIGINFO : return "SIGINFO BSD signal。由Status Key產(chǎn)生,通常是CTRL+T。發(fā)送給所有Foreground Group的進(jìn)程 ";
case SIGINT : return "SIGINT 由Interrupt Key產(chǎn)生,通常是CTRL+C或者DELETE";
case SIGIO : return "SIGIO 異步IO事件";
//case SIGIOT : return "SIGIOT 實(shí)現(xiàn)相關(guān)的硬件異常,一般對(duì)應(yīng)SIGABRT ";
case SIGKILL : return "SIGKILL 強(qiáng)制中止";
case SIGPIPE : return "SIGPIPE 在reader中止之后寫(xiě)Pipe的時(shí)候發(fā)送";
//case SIGPOLL : return "SIGPOLL 當(dāng)某個(gè)事件發(fā)送給Pollable Device的時(shí)候發(fā)送 ";
case SIGPROF : return "SIGPROF Setitimer指定的Profiling Interval Timer所產(chǎn)生";
case SIGPWR : return "SIGPWR 和系統(tǒng)相關(guān)。和UPS相關(guān)。";
case SIGQUIT : return "SIGQUIT 輸入Quit Key(CTRL+\\)";
case SIGSEGV : return "SIGSEGV 非法內(nèi)存訪問(wèn)";
case SIGSTOP : return "SIGSTOP 中止進(jìn)程";
case SIGSYS : return "SIGSYS 非法系統(tǒng)調(diào)用";
case SIGTERM : return "SIGTERM 請(qǐng)求中止進(jìn)程,kill命令缺省發(fā)送";
case SIGTRAP : return "SIGTRAP 實(shí)現(xiàn)相關(guān)的硬件異常。一般是調(diào)試異常";
case SIGTSTP : return "SIGTSTP Suspend Key,一般是Ctrl+Z";
case SIGTTIN : return "SIGTTIN 當(dāng)Background Group的進(jìn)程嘗試讀取Terminal的時(shí)候發(fā)送";
case SIGTTOU : return "SIGTTOU 當(dāng)Background Group的進(jìn)程嘗試寫(xiě)Terminal的時(shí)候發(fā)送";
case SIGURG : return "SIGURG 當(dāng)out-of-band data接收的時(shí)候可能發(fā)送";
case SIGUSR1 : return "SIGUSR1 用戶自定義signal 1";
case SIGUSR2 : return "SIGUSR2 用戶自定義signal 2";
case SIGVTALRM : return "SIGVTALRM setitimer函數(shù)設(shè)置的Virtual Interval Timer超時(shí)的時(shí)候";
case SIGWINCH : return "SIGWINCH 當(dāng)Terminal的窗口大小改變的時(shí)候,發(fā)送給Foreground Group的所有進(jìn)程";
case SIGXCPU : return "SIGXCPU 當(dāng)CPU時(shí)間限制超時(shí)的時(shí)候";
case SIGXFSZ : return "SIGXFSZ 進(jìn)程超過(guò)文件大小限制";
default: return "未知的信號(hào)";
}
}
其中屏蔽掉了一些是因?yàn)榧嫒菪詥?wèn)題(這段代碼以前要在IBM、SUN、HP的小型機(jī)上運(yùn)行,后來(lái)才改在Linux上運(yùn)行),你可以根據(jù)需要添加。
到此這篇關(guān)于C/C++實(shí)現(xiàn)捕獲所有信號(hào)的示例詳解的文章就介紹到這了,更多相關(guān)C++捕獲所有信號(hào)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)24位彩色圖像二值化
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)24位彩色圖像二值化,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
Qt5連接并操作PostgreSQL數(shù)據(jù)庫(kù)的實(shí)現(xiàn)示例
本文主要介紹了Qt5連接并操作PostgreSQL數(shù)據(jù)庫(kù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
C語(yǔ)言修煉之路悟徹?cái)?shù)組真妙理?巧用下標(biāo)破萬(wàn)敵上篇
在C語(yǔ)言和C++等語(yǔ)言中,數(shù)組元素全為指針變量的數(shù)組稱為指針數(shù)組,指針數(shù)組中的元素都必須具有相同的存儲(chǔ)類型、指向相同數(shù)據(jù)類型的指針變量。指針數(shù)組比較適合用來(lái)指向若干個(gè)字符串,使字符串處理更加方便、靈活2022-02-02
關(guān)于C++友元類的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++友元類的實(shí)現(xiàn)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
C++實(shí)現(xiàn)LeetCode(139.拆分詞句)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(139.拆分詞句),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言中字符和字符串處理(ANSI字符和Unicode字符)
這篇文章主要介紹了C語(yǔ)言與C++中字符和字符串處理(ANSI字符和Unicode字符)的詳細(xì)內(nèi)容,非常的全面,這里推薦給大家,希望大家能夠喜歡。2015-03-03
C++實(shí)現(xiàn)LeetCode(187.求重復(fù)的DNA序列)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(187.求重復(fù)的DNA序列),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

