4

In one of my programs, I would hit "SIGBUS" when trying to access a mmap-ed memory location that failed to get the memory page (as the underlying physical memory ran out) and the program crashed due to SIGBUS.

I plan to register a SIGBUG signal handler to avoid crash. However, I don't want to exit() the program from the SIGBUS handler. I am trying to see if there is anyway to gracefully report ENOMEM and continue the program with other work.

Can I do the following? The code looks like this:

mem_p->head = MY_HEAD_MAGIC;   /* this line could trigger SIGBUS */
if (sigbus_happened) {
    sigbus_happened = FALSE;
    do_something_else();   
    return ENOMEM;
}

and the signal handler:

void signal_handler (int sig)
{
   if (sig == SIGBUS)
      sigbus_happened = TRUE;
}   

Would the above work and no crash?

Thanks.

4

1 回答 1

3

您显示的代码可能会与您的期望背道而驰。这是因为编译器可以自由地排列代码,以便它“记住”sigbus_happened赋值之前的值mem_p->head。因此,即使信号处理程序执行,您的代码也可能检测不到标志已设置。至少,您需要制作变量volatile.

最好是简单地检查mmap()呼叫是否失败。您可以通过检查调用是否返回值来做到这一点MAP_FAILED。如果调用失败,请不要尝试访问指针值。

您尝试捕获SIGBUS提醒异常处理。C 没有 C++ 风格的异常处理(尽管存在模仿它们的宏包,例如cexcept )。但是,以一种更像异常工作方式的方式遵循您的模型的一种方法是使用setjmp()and longjmp()setjmp()保存现有的堆栈上下文并返回0. longjmp()将代码返回到保存的上下文,并导致setjmp()返回非0值。

从信号处理程序中,最好使用 POSIX sigsetjmp()siglongjmp()以便在调用信号处理程序之前被 C 运行时或操作系统阻塞的任何信号在返回到保存的上下文时重置为它们所具有的值。

jmp_buf *sigbus_jmp; // global

void signal_handler (int sig)
{
   if (sig == SIGBUS) {
      if (sigbus_jmp) siglongjmp(*sigbus_jmp, 1);
      // no one to catch the error, so abort
      abort();
    }
}

    //...
    jmp_buf sigbus_jmpbuf;
    sigbus_jmp = &sigbus_jmpbuf;
    if (sigsetjmp(sigbus_jmpbuf, 1) == 0) {
        // try
        mem_p->head = MY_HEAD_MAGIC;   /* this line could trigger SIGBUS */
    } else {
        // catch
        do_something_else();   
        return ENOMEM;
    }
    sigbus_jmp = 0;
于 2013-10-17T00:49:14.150 回答