如果我捕获SIGSEGV且信号处理程序引起另一个SIGSEGV会发生什么?

6

这个问题是针对Linux的,使用GCC编译器。

如果在一个本来用于捕获SIGSEGV的信号处理程序中发生了SIGSEGV(我指的是通常会导致SIGSEGV的违规行为),会发生什么行为?以下是一个代码示例,以帮助讨论:

/* In main or whatever */
{
  struct sigaction sa = {}; /* initialised to all zero (I vote for GCC style breach of standard here) */
  sa.sa_handler = DisasterSignals;
  sa.sa_flags = SA_RESETHAND | SA_NODEFER;  /* To have or have not */
  sigaction(SIGSEGV, &sa, NULL);
}

static void DisasterSignals(int signal)
{
  /* We cannot save the situation, the purpose of catching the signal is
     only to do something clever to aid debugging before we go. */

  /* Q: What if we segfault in here?? */

  abort(); /* This should give us the expected core dump (if we survive to this point) */
}

想象一下,在点“Q”处有一个有问题的机器指令。

1)没有使用SA_RESETHAND | SA_NODEFER:这似乎将系统置于逻辑陷阱中:在“Q”处,应该生成SIGSEGV信号。但是,信号处理程序中阻止了SIGSEGV信号(默认的sigaction行为)。执行如何继续?会冻结吗?它会跳过有问题的指令吗(我猜测不会)?

2)使用SA_RESETHAND | SA_NODEFER:我猜在这种情况下,当SIGSEGV信号重复时,程序将以“正常”的方式崩溃。

3)只使用SA_NODEFER:我猜在这种情况下,当SIGSEGV信号重复时,信号处理程序将被递归调用;如果SIGSEGV信号总是重复,则会冻结直到堆栈溢出,然后会发生什么。


3
"Quis custodiet ipsos custodes?" 翻译成英文是 "Who will guard the guards themselves?"。 - Weather Vane
1
在信号处理程序中,您只应调用可重入函数。 - edmz
1
我的结果显示:1和2 - 处理程序中的段错误会立即使程序崩溃。3 - 信号处理程序递归约13k次(变化),然后崩溃。 - zch
1
http://man7.org/linux/man-pages/man2/sigprocmask.2.html - 逻辑陷阱的答案:“如果在信号被阻塞时生成了SIGBUS、SIGFPE、SIGILL或SIGSEGV,则结果是未定义的,除非信号是由kill(2)、sigqueue(3)或raise(3)生成的。” - zch
1
你不能将评论标记为被接受的答案,但你可以标记答案。 - zch
显示剩余3条评论
1个回答

5
默认情况下,在处理信号时,它会被屏蔽,因此无法递归触发。如果程序执行触发了被屏蔽的信号(无效的内存访问、段错误、除以 0 等),则行为是不确定的:
如果在阻塞 SIGBUS、SIGFPE、SIGILL 或 SIGSEGV 时生成它们,则结果是不确定的,除非该信号是由 kill(2)、sigqueue(3) 或 raise(3) 生成的。
在我的系统上,这会导致进程崩溃。
使用 SA_NODEFER 就没有屏蔽,因此信号可以被递归处理,直到堆栈溢出。添加 SA_RESETHAND 将恢复默认操作(对于 SIGSEGV 是崩溃)。
我将您的示例调整为简单的测试程序,以便您可以验证此行为:
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

volatile char *ptr;

static void DisasterSignals(int signal)
{
  /* We cannot save the situation, the purpose of catching the signal is
     only to do something clever to aid debugging before we go. */
  write(1, "11\n", 3);
  *ptr = 1;
  write(1, "13\n", 3);
  abort(); /* This should give us the expected core dump (if we survive to this point) */
}

struct sigaction sa = {}; /* initialised to all zero (I vote for GCC style breach of standard here) */

int main()
{
  sa.sa_handler = DisasterSignals;
  sa.sa_flags = /*SA_RESETHAND | */SA_NODEFER;  /* To have or have not */
  sigaction(SIGSEGV, &sa, NULL);

  write(1, "25\n", 3);
  *ptr = 1;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接