C/C++实现捕获所有信号的示例详解
一、原理
Linux的信号可能在你无法意识到的情况下发生。
比如socket网络断开,默认情况下发送SIGPIPE给正在send/recv的进程从而杀死进程,忘记了处理这个就很麻烦,程序大部分时间很正常,偶尔奇怪地终止,可能要花很大力气才会找到原因。
而Linux的信号是不带任何数据的,知道一个连接断开又能怎么样呢?又不知道是哪个连接断开(其实我们会根据send/recv的返回值处理的嘛)。
所以为了省力,我们可以在程序开始处捕获所有信号,然后再根据实际运行时的情况针对性处理(大部分情况根本不需要任何处理)。
关于信号的更多知识在后面的“信号描述”函数里。
二、基础
处理信号最简单的方式就是调用signal函数,这个函数设置信号处理方法并返回之前的信号处理方法(以便你可以在执行自己的处理之前或之后调用原来的处理方法从而形成调用链)。
#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 << "未处理的异常发生" << 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。发送给所有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的时候发送"; //case SIGPOLL : return "SIGPOLL 当某个事件发送给Pollable Device的时候发送 "; case SIGPROF : return "SIGPROF Setitimer指定的Profiling Interval Timer所产生"; case SIGPWR : return "SIGPWR 和系统相关。和UPS相关。"; case SIGQUIT : return "SIGQUIT 输入Quit Key(CTRL+\\)"; case SIGSEGV : return "SIGSEGV 非法内存访问"; case SIGSTOP : return "SIGSTOP 中止进程"; case SIGSYS : return "SIGSYS 非法系统调用"; case SIGTERM : return "SIGTERM 请求中止进程,kill命令缺省发送"; case SIGTRAP : return "SIGTRAP 实现相关的硬件异常。一般是调试异常"; case SIGTSTP : return "SIGTSTP Suspend Key,一般是Ctrl+Z"; case SIGTTIN : return "SIGTTIN 当Background Group的进程尝试读取Terminal的时候发送"; case SIGTTOU : return "SIGTTOU 当Background Group的进程尝试写Terminal的时候发送"; case SIGURG : return "SIGURG 当out-of-band data接收的时候可能发送"; 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的窗口大小改变的时候,发送给Foreground Group的所有进程"; case SIGXCPU : return "SIGXCPU 当CPU时间限制超时的时候"; case SIGXFSZ : return "SIGXFSZ 进程超过文件大小限制"; default: return "未知的信号"; } }
其中屏蔽掉了一些是因为兼容性问题(这段代码以前要在IBM、SUN、HP的小型机上运行,后来才改在Linux上运行),你可以根据需要添加。
到此这篇关于C/C++实现捕获所有信号的示例详解的文章就介绍到这了,更多相关C++捕获所有信号内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论