2013-10-17 451 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