jmp_buf functjmp;

void sigsegv_handler(int sig) {
  sio_printf("Caught sigsegv!\n");
  siglongjmp(functjmp, 2);
  return;
}

void foo(unsigned val) {
  assert(0);
  sio_printf("entered!\n");
}

int main() {
  struct sigaction action;

  action.sa_handler = sigsegv_handler;
  sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
  sigaddset(&action.sa_mask, SIGSEGV);
  action.sa_flags = SA_RESTART; /* Restart syscalls if possible */

  if (sigaction(SIGSEGV, &action, NULL) < 0) {
    sio_fprintf(stderr, "handler error!\n");
  }
  sigset_t prev_mask;
  sigprocmask(SIG_BLOCK, NULL, &prev_mask);
  if (sigsetjmp(functjmp, 0) == 0) {
    foo(*(unsigned *)0x8);
  } {
    sigprocmask(SIG_BLOCK, &prev_mask, NULL);
    sio_printf("jump handled!\n");
    foo(*(unsigned *)0x8);
  }
  sio_fprintf(stderr, "how did it come here?!\n");
}


我一直在使用gdb调试此代码,并且我无法弄清楚为什么该程序将不会使用自己的处理程序来处理第二个SIGSEGV信号,假设该程序未接收或发送其他信号?任何带有sio前缀的功能都是stdio对应版本的异步安全变体。
目前,我猜想它与我从信号处理程序返回的概念中所缺少的东西有关,而longjmp根本不这样做。

最佳答案

简短答案:SIGSEGV for C程序通常无法恢复。使用C ++可能会获得更多收益。

长答案:请参见Coming back to life after Segmentation Violation中的讨论

假设可以承担不确定行为的风险:

可以重新启用SEGV。核心问题是在信号处理程序期间,代码明确阻止了SEGV信号被触发(使用sigaddset)。另外,(信号处理程序的)默认行为是在信号处理期间,相同的信号处理将推迟到信号处理程序返回之前。在OP代码中,信号处理程序从不返回(因为siglongjmp)

这两个问题都可以通过更改原始代码来解决。

  // Make sure all attributes are NULL.
  struct sigaction action = {} ;

  action.sa_handler = sigsegv_handler;
  sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
  // Not Needed:: sigaddset(&action.sa_mask, SIGSEGV);
  // Add SA_NODEFER to disable the deferred processing of SIGSEGV.
  action.sa_flags = SA_RESTART | SA_NODEFER ; /* Restart syscalls if possible */

  // rest of code here
  if (sigaction(SIGSEGV, &action, NULL) < 0) {
    sio_fprintf(stderr, "handler error!\n");
  }
  ...

10-07 14:14
查看更多