发生段错误后程序不崩溃

一个段错误的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *tmp = NULL;
	strcpy(tmp, "hello");
	return 0;
}

执行结果 

加上段错误处理函数: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

void sig_handle(int sig)
{
	printf("sig_handle %d\n", sig);
}

int main()
{
	char *tmp = NULL;
	signal(SIGSEGV, sig_handle);
	strcpy(tmp, "hello");
	return 0;
}

执行结果:

程序陷入死循环打印, 是因为执行strcpy函数时会产生SIGSEGV信号,因为之前我们注册了SIGSEGV型号处理函数,所有会执行注册的sig_handle信号处理函数,处理完之后会返回到产生段错误的地方,然后接着会产生SIGSEGV信号。。。

正确的处理函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

void sig_handle(int sig)
{
	printf("sig_handle %d\n", sig);
	exit(0);
}

int main()
{
	char *tmp = NULL;
	signal(SIGSEGV, sig_handle);
	strcpy(tmp, "hello");
	return 0;
}

 但是我们有时候不想让程序退出怎么办呢?那么就需要如下两个函数:

#include <setjmp.h>
 int setjmp(jmp_buf env); 
 void longjmp(jmp_buf env, int val);

int setjmp(jmp_buf env);  这个函数 将上下文 ,就是cpu和内存的信息保存到env中 (不用去理解 jmp_buf,就当我们平时用的buff好了),然后调用 void longjmp(jmp_buf env, int val); 的时候 跳转到使用env中的信息 ,恢复上下文 。如果是第一回调用setjmp 它会返回 0,如果是在 从longjmp 跳转过来的 ,那就返回 longjmp的参数 val,根据setjmp的返回值 我们就可以决定执行可能发生错误的代码还是直接跳过这段代码 。

扫描二维码关注公众号,回复: 12774338 查看本文章
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>

jmp_buf env;

void sig_handle(int sig)
{
	printf("sig_handle %d\n", sig);
	longjmp(env, 1);
}

int main()
{
	char *tmp = NULL;
	
	signal(SIGSEGV, sig_handle);
	int ret = setjmp(env);
	printf("ret = %d\n", ret);
	if(ret == 0)
	{
		strcpy(tmp, "hello");
	}
	else
	{
		printf("after SIGSEGV\n");
	}
	
	return 0;
}

但是像下面这样使用还是有问题的

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>

jmp_buf env;

void sig_handle(int sig)
{
	printf("sig_handle %d\n", sig);
	longjmp(env, 1);
}

int main()
{
	char *tmp = NULL;
	signal(SIGSEGV, sig_handle);
	while(1)
	{
		int ret = setjmp(env);
		printf("ret = %d\n", ret);
		if(ret == 0)
		{
			strcpy(tmp, "hello");
		}
		else
		{
			printf("after SIGSEGV\n");
		}
		sleep(1);
	}
	return 0;
}

 是因为程序产生SIGSEGV 信号进入到信号处理函数之后 这个信号会被 阻塞(block) 直到信号处理函数返回”,在进入到信号处理函数之后 ,这个时候 系统阻塞了 SIGSEGV 这个信号 ,当跳回到 int r = setjmp(env); 这行代码的时候  SIGSEGV 信号依然是阻塞的 ,那以后 再给他绑定信号处理函数 自然没有作用。

我们可以使用如下函数解决上述问题:

int sigsetjmp(sigjmp_buf env, int savesigs);
void siglongjmp(sigjmp_buf env, int val);

这两个函数和setjmp函数longjmp函数的唯一区别是sigsetjmp函数多了一个参数savesigs, 当 savesigs 不为 0时,会保存当前的信号屏蔽表 (signal mask),然后在使用siglongjmp 跳转的时候 会恢复 线程的 屏蔽表。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>

jmp_buf env;

void sig_handle(int sig)
{
	printf("sig_handle %d\n", sig);
	siglongjmp(env, 1);
}

int main()
{
	char *tmp = NULL;
	signal(SIGSEGV, sig_handle);
	while(1)
	{
		int ret = sigsetjmp(env, 1);
		printf("ret = %d\n", ret);
		if(ret == 0)
		{
			strcpy(tmp, "hello");
		}
		else
		{
			printf("after SIGSEGV\n");
		}
		sleep(1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/u014608280/article/details/103960219