这是我的代码,
#include<signal.h>
#include<stdio.h>
int main(int argc,char ** argv)
{
char *p=NULL;
signal(SIGSEGV,SIG_IGN); //Ignoring the Signal
printf("%d",*p);
printf("Stack Overflow"); //This has to be printed. Right?
return 0;
}
在执行代码时,我遇到了分段错误。我使用SIG_IGN忽略了信号。所以我不应该得到分段错误。对?然后,打印'* p'值后的printf()
语句也必须执行。对?
答案 0 :(得分:20)
您的代码忽略了SIGSEGV而不是捕获它。回想一下,在处理完信号后重启了触发信号的指令。在你的情况下,处理信号没有改变任何东西,所以下次尝试违规指令时,它会以同样的方式失败。
如果你打算抓住信号改变这个
signal(SIGSEGV, SIG_IGN);
到这个
signal(SIGSEGV, sighandler);
您也应该使用sigaction()
代替signal()
。参见相关手册页。
在您的情况下,违规指令是尝试取消引用NULL指针的指令。
printf("%d", *p);
以下内容完全取决于您的平台。
您可以使用gdb
来确定特定汇编指令触发信号的内容。如果您的平台与我的平台类似,您会发现指令是
movl (%rax), %esi
rax寄存器保持值为0,即NULL
。在信号处理程序中修复此问题的一种(非便携!)方法是使用处理程序获得的第三个参数信号,即用户上下文。这是一个例子:
#include <signal.h>
#include <stdio.h>
#define __USE_GNU
#include <ucontext.h>
int *p = NULL;
int n = 100;
void sighandler(int signo, siginfo_t *si, ucontext_t* context)
{
printf("Handler executed for signal %d\n", signo);
context->uc_mcontext.gregs[REG_RAX] = &n;
}
int main(int argc,char ** argv)
{
signal(SIGSEGV, sighandler);
printf("%d\n", *p); // ... movl (%rax), %esi ...
return 0;
}
此程序显示:
Handler executed for signal 11
100
它首先通过尝试取消引用NULL地址来执行处理程序。然后,处理程序通过将rax设置为变量n
的地址来修复问题。一旦处理程序返回,系统就会重试违规指令,这次成功。 printf()
收到100作为其第二个参数。
我强烈建议您不要在程序中使用此类非便携式解决方案。
答案 1 :(得分:14)
您可以忽略该信号,但您必须对此进行一些操作。我相信您在发布的代码中所做的事情(忽略SIGSEGV
通过SIG_IGN
)将无法正常工作,原因在阅读粗体项目后会变得明显。< / p>
当你做一些导致内核向你发送SIGSEGV的东西时:
因此,如果你没有做任何事情,它就会不断循环。如果您执行捕获SIGSEGV
并且您没有退出,从而干扰正常流程,您必须:
答案 2 :(得分:10)
另一种选择是将风险操作括在setjmp / longjmp中,即
#include <setjmp.h>
#include <signal.h>
static jmp_buf jbuf;
static void catch_segv()
{
longjmp(jbuf, 1);
}
int main()
{
int *p = NULL;
signal(SIGSEGV, catch_segv);
if (setjmp(jbuf) == 0) {
printf("%d\n", *p);
} else {
printf("Ouch! I crashed!\n");
}
return 0;
}
这里的setjmp / longjmp模式类似于try / catch块。但它风险很大,如果您的风险函数超出堆栈,或者在资源被释放之前分配资源但崩溃,则不会挽救您。最好检查你的指针,而不是通过坏指针进行间接检查。
答案 3 :(得分:1)
尝试忽略或处理SIGSEGV是错误的方法。由程序始终触发的SIGSEGV表示错误。在您的代码中或您委托的代码中。触发错误后,任何事情都可能发生。信号处理程序无法执行合理的“清除”或修复操作,因为它不知道在何处触发了信号或执行了什么操作。最好的办法就是让程序快速失败,这样程序员就可以在仍然处于立即失败状态的情况下对其进行调试,而不必等到导致失败的原因使程序以后(可能)失败。模糊。通过不尝试忽略或处理信号,可以使程序快速失败。