套路化编程:C/C++ 捕获所有信号(源码)

news/2024/5/18 22:29:12 标签: C/C++, 信号处理, signal

        Linux的信号机制大部分情况下用不到,但是由于大部分信号的默认处理是终止进程,不正确处理会惹麻烦。

目录

一、原理

二、基础

三、代码

捕获全部信号的代码

调用代码

信号处理函数

信号描述


一、原理

        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上运行),你可以根据需要添加。

(这里是结束)


http://www.niftyadmin.cn/n/5415137.html

相关文章

URL输入到页面渲染过程详解

当我们在浏览器中输入一个URL并按下回车键时&#xff0c;浏览器会执行一系列步骤来解析URL、发送请求、接收响应&#xff0c;并最终渲染页面。这个过程涉及到多个阶段&#xff0c;包括DNS解析、TCP握手、发送HTTP请求、服务器处理请求、返回HTTP响应、浏览器解析和渲染等。下面…

蓝桥集训之小朋友排队

蓝桥集训之小朋友排队 核心思想&#xff1a;归并排序 可证明每个小朋友交换的次数为 左边比他高的人数 右边比他低的人数所以求出上述值 再k*(k1)/2 求和即可简化为用归并排序求人数 #include<iostream>#include<algorithm>#include<cstring>#define x f…

电脑小问题:Windows更新后黑屏

Windows 更新后黑屏解决方法 在 Windows 更新后&#xff0c;伴随了一个小问题&#xff0c;电脑启动后出现了桌面黑屏。原因可能是火绒把 explorer.exe 当病毒处理了。 下面讲解 Windows 更新后黑屏的解决方法&#xff0c;步骤如下&#xff1a; 1. 按 ctrl alt delete 组合键…

openlayers加载cesiumlab切片

openlayers可以通过xyz的方式加载nginx发布的cesiumlab切片。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

植物病虫害:YOLO玉米病虫害识别数据集

玉米病虫害识别数据集&#xff1a;玉米枯萎病&#xff0c;玉米灰斑病&#xff0c;玉米锈病叶&#xff0c;粘虫幼虫&#xff0c;玉米条斑病&#xff0c;黄二化螟&#xff0c;黄二化螟幼虫7类&#xff0c;yolo标注完整&#xff0c;3900多张图像&#xff0c;全部原始数据&#xff…

RK平台内核解压方式

64 位平台 64 位平台的机器通常烧写Image&#xff0c;由U-Boot 加载到目标运行地址。但是 RK平台的 U-Boot 还可支持 对64位 LZ4格式的压缩内核进行解压。但是用户必须使能&#xff1a; CONFIG_LZ4y64位LZ4压缩内核的解压前、后地址必须定义在各平台的 rkxxx_common.h 文件中…

MySQL--优化(索引--索引失效场景)

MySQL–优化&#xff08;索引–索引失效场景&#xff09; 定位慢查询SQL执行计划索引 存储引擎索引底层数据结构聚簇和非聚簇索引索引创建原则索引失效场景 SQL优化经验 常见的索引失效场景 1、场景准备&#xff1a; 给 tb_user 表创建联合索引&#xff0c;字段为&#xff1…

kafka报文模拟工具的使用

日常项目中经常会碰到消费kafka某个topic的数据&#xff0c;如果知道报文格式&#xff0c;即可使用工具去模拟发送报文&#xff0c;以此测试代码中是否能正常消费到这个数据。 工具资源已上传&#xff0c;可直接访问连接下载&#xff1a;https://download.csdn.net/download/w…