【问题标题】:What happens if I catch SIGSEGV and the signal handler causes another SIGSEGV?如果我捕获 SIGSEGV 并且信号处理程序导致另一个 SIGSEGV 会发生什么?
【发布时间】:2016-01-20 20:04:08
【问题描述】:

这个问题是针对 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 总是重复,我们会冻结,直到堆栈溢出,然后是什么。

【问题讨论】:

  • Quis custodiet ipsos custodes?
  • 您应该只在信号处理程序中调用可重入函数。
  • coliru.stacked-crooked.com/a/40fc467ac87e9cfa 我的结果显示:1&2 - 处理程序中的段错误立即使程序崩溃。 3 - 信号处理程序递归大约 13k 次(变化)然后崩溃。
  • man7.org/linux/man-pages/man2/sigprocmask.2.html - 逻辑陷阱的答案:" 如果 SIGBUS、SIGFPE、SIGILL 或 SIGSEGV 在被阻塞时生成,则结果未定义,除非信号是由 kill(2) 生成的、sigqueue(3) 或 raise(3)。"
  • 您不能将 cmets 标记为已接受的答案,但您可以标记答案。

标签: c linux


【解决方案1】:

默认情况下,在处理信号时,它会被屏蔽,因此不能递归触发。如果屏蔽信号由程序执行触发(无效的内存访问、段错误、除以 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;
}

【讨论】:

    猜你喜欢
    • 2017-08-02
    • 1970-01-01
    • 2020-08-31
    • 2010-12-06
    • 2019-08-27
    • 2023-03-29
    相关资源
    最近更新 更多