C/C++实现捕获所有信号的示例详解

 更新时间:2024年03月08日 16:15:26   作者:初级代码游戏  
Linux的信号机制大部分情况下用不到,但是由于大部分信号的默认处理是终止进程,不正确处理会惹麻烦,所以我们来看看如何使用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++捕获所有信号内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++异常使用详解(看这一篇就够了)

    C++异常使用详解(看这一篇就够了)

    C++中的异常是指在程序执行过程中发生错误,导致程序无法正常运行的情况,下面这篇文章主要给大家介绍了关于C++异常使用的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • [c++]变量声明与定义的规则详解

    [c++]变量声明与定义的规则详解

    这篇文章主要介绍了[c++]变量声明与定义的规则详解,对于学习c++的朋友来说这是一个很细腻的文章,代码完整,需要的朋友可以参考下
    2021-04-04
  • C++使用boost::lexical_cast进行数值转换

    C++使用boost::lexical_cast进行数值转换

    这篇文章介绍了C++使用boost::lexical_cast进行数值转换的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C++之Primer算术运算符详解

    C++之Primer算术运算符详解

    这篇文章主要介绍了C++之Primer算术运算符方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • C语言大小端模式、判断大小端、大小端转换方法详解

    C语言大小端模式、判断大小端、大小端转换方法详解

    这篇文章主要介绍了C语言大小端模式、判断大小端、大小端转换的相关资料,大端和小端是数据在内存中的存储方式,大端模式下高字节存于低地址,小端模式则相反,大小端问题由数据类型多字节存储引起,不同选择形成不同存储模式,需要的朋友可以参考下
    2024-10-10
  • c++代码实现tea加密算法的实例详解

    c++代码实现tea加密算法的实例详解

    这篇文章主要介绍了c++代码实现tea加密算法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • C语言数组的各种操作梳理

    C语言数组的各种操作梳理

    数组是一组有序的数据的集合,数组中元素类型相同,由数组名和下标唯一地确定,数组中数据不仅数据类型相同,而且在计算机内存里连续存放,地址编号最低的存储单元存放数组的起始元素,地址编号最高的存储单元存放数组的最后一个元素
    2022-04-04
  • C语言实现数组元素排序方法详解

    C语言实现数组元素排序方法详解

    这篇文章主要为大家介绍了C语言算法练习中数组元素排序的实现方法,文中的示例代码讲解详细,对我们学习C语言有一定帮助,需要的可以参考一下
    2023-02-02
  • Qt图形图像开发之Qt曲线图美化QChart QScatterSeries 空心点阵图,鼠标移动到上面显示数值,鼠标移开数值消失效果实例

    Qt图形图像开发之Qt曲线图美化QChart QScatterSeries 空心点阵图,鼠标移动到上面显示数值,鼠标移开

    这篇文章主要介绍了Qt图形图像开发之Qt曲线图美化QChart QScatterSeries 空心点阵图,鼠标移动到上面显示数值,鼠标移开数值消失效果实例,需要的朋友可以参考下
    2020-03-03
  • OpenCV图像处理之实现图像膨胀腐蚀操作

    OpenCV图像处理之实现图像膨胀腐蚀操作

    图像形态学操作是指基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学对图像进行处理。本文将为大家介绍一下如何利用OpenCV实现其中的腐蚀和膨胀操作,需要的可以参考一下
    2022-09-09

最新评论