为什么我在下面的C代码中连续获得SIGSEGV

时间:2019-07-07 01:34:09

标签: c linux signals

我正在尝试学习信号。我知道无效的内存访问将导致段错误。因此,我为SIGSEGV信号注册了一个信号处理程序。

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


void sighandler(int signum)
{
    printf("%s\n", __func__);
}

int main()
{
    int *a = NULL;
    signal(SIGSEGV, sighandler);
    *a = 5;
    return 0;
}

运行此代码,我不断收到SIGSEGV信号。我以为我应该只收到一次信号。你们能解释为什么我不断收到信号吗

2 个答案:

答案 0 :(得分:4)

SEGV处理程序完成后,触发的指令将重新执行。由于您没有采取任何措施来防止下次执行出错,因此您将无限制地再次获得SEGV。

this answer中查看更多信息。

答案 1 :(得分:2)

信号处理程序返回到触发它的指令,即*a = 5,这导致它循环。

您有几个问题,包括在信号处理程序中使用printf

有安全和不安全的方法来处理此问题

注释

通常不建议使用signal(2)进行信号处理。

由于信号语义的工作方式,处理SIGSEGV更加复杂。从手册页引用:

  

signal()的唯一可移植用法是将信号的处置方式设置为SIG_DFL或SIG_IGN。使用signal()时的语义   建立一个不同的信号处理程序   系统(POSIX.1明确允许这种变化);不要为此目的使用它。

     

POSIX.1通过指定sigaction(2)解决了可移植性问题,当sigaction(2)   信号处理程序被调用;使用该接口而不是signal()。

因此,您应该做的第一件事就是使用sigaction

接下来,处理SIGSEGV是一个奇怪的野兽:

How to write a signal handler to catch SIGSEGV?

Does linux allow any system call to be made from signal handlers?

有很好的答案,并深入了解具体细节。那里给出的一些答案中有外部链接。

如何使用signal(2)

好吧:-)假设您想要使用signal(2),而您想要以一种怪异的方式玩这个游戏 ....

您可以使用sigjmpsetsiglongjmp

sigjmpset标记了siglongjmp应该跳转到的位置。第一次调用sigjmpset(以设置点)时,它返回0。siglongjmp跳转到它时(这意味着它由于跳远而再次被调用),它返回1。

这意味着我们可以做到:

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

sigjmp_buf env;
int sigsav;

void sighandler(int signum)
{

    const char msg[] = "Skipping signal\n";
    write(2, msg, sizeof(msg));
    siglongjmp(env, sigsav);
}

int main()
{
    int *a = NULL;

    signal(SIGSEGV, sighandler);
    if(!sigsetjmp(env, sigsav)) {
        printf("setting value of a\n");
    *a = 5;
    }
    else {
    printf("returned to sigsetjmp, but now we skip it!\n");
    }
    return 0;
}