2013-10-17 108 views
4

在我的一个程序中,当试图访问无法获取内存页的mmap-ed内存位置时(因为底层物理内存用完)并且程序崩溃,我会点击“SIGBUS” SIGBUS。在linux中处理SIGBUS

我计划注册一个SIGBUG信号处理程序以避免崩溃。但是,我不想从SIGBUS处理程序退出()该程序。我试图看看是否有任何优雅地报告ENOMEM并继续与其他工作的计划。

我可以做以下吗?代码如下所示:

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

和信号处理程序:

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

将在上述工作并没有崩溃?

谢谢。

+1

简答:不。只要确保你没有对mmapp()ed页面进行处理即可,并且不要依赖任何欺骗手段。真。 – wildplasser

+2

@wildplasser如果你使用MAP_NORESERVE SIGBUS是可能的,当系统没有足够的内存+交换(尽管如果你使用所有的交换你有比SIGBUS更大的问题) – Eloff

回答

3

存在这样的危险,即您显示的代码可能与您的期望相反。这是因为编译器可以自由安排代码,以便在分配mem_p->head之前“记住”sigbus_happened的值。所以,即使信号处理程序执行时,您的代码也可能无法检测到该标志已设置。至少,您需要制作变量volatile

更好的方法是简单地检查mmap()调用是否失败。您可以通过检查呼叫是否返回值MAP_FAILED来完成此操作。如果调用失败,请不要尝试访问指针值。

你试图捕获SIGBUS提醒异常处理。 C没有C++风格的异常处理(尽管存在模拟它们的宏包,例如cexcept)。但是,以更像异常工作方式的方式跟踪您的模型的一种方法是使用setjmp()longjmp()setjmp()保存现有堆栈上下文并返回0longjmp()将代码返回到保存的上下文,并导致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; 
+2

更好地使用[siglongjmp](http:// pubs。 opengroup.org/onlinepubs/7908799/xsh/siglongjmp.html)在信号处理程序(带'sigsetjmp') –

+0

谢谢jxh。我会尝试这种方法。顺便说一下,我的代码已经检查了mmap()失败并已经处理了。问题是:即使mmap()成功,如果物理内存用完或底层tmpfs已满,内存访问仍可能生成SIGBUS。在我的理解中,mmap()并不保证底层的物理内存总是可用的。是对的吗? – user1783732

+0

@ user1783732:使用“MAP_LOCKED”标志。 – jxh