2014-01-18 40 views
2

我的系統是Ubuntu 12.04。 我修改了man 2 signalfd的示例,並在示例中添加了sigaddset(&mask, SIGSEGV)。但是當生成SIGSEGV時,我無法獲得輸出。爲什麼無法使用signalfd捕獲SIGSEGV?

這是一個缺陷glibc?的源代碼的片段下列:

 sigemptyset(&mask); 
     sigaddset(&mask, SIGINT); 
     sigaddset(&mask, SIGQUIT); 
     sigaddset(&mask, SIGSEGV); 

     /* Block signals so that they aren't handled 
      according to their default dispositions */ 

     if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 
      handle_error("sigprocmask"); 

     sfd = signalfd(-1, &mask, 0); 
     if (sfd == -1) 
      handle_error("signalfd"); 
     int* a = NULL; 
     for (;;) { 
      s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); 
      if (s != sizeof(struct signalfd_siginfo)) 
       handle_error("read"); 

      if (fdsi.ssi_signo == SIGINT) { 
       printf("Got SIGINT\n"); 
       (*a) = 1; 
      } else if (fdsi.ssi_signo == SIGQUIT) { 
       printf("Got SIGQUIT\n"); 
       exit(EXIT_SUCCESS); 
      } else { 
       printf("Read unexpected signal\n"); 
      } 
     } 
+1

請解釋你爲什麼要抓住'SIGSEGV' .... –

+1

我只是想測試是否可以異步處理「SIGSEGV」並輸出堆棧回溯信息。因爲有許多「不安全的功能」在信號處理程序中不能使用。 –

+1

正如我在答覆中所說的,爲了回溯目的,您可以使用SIGSEGV處理程序中的不安全函數。這通常運作良好(但可能會失敗或崩潰) –

回答

4

參見thisthat答案爲詳細的說明。請仔細閱讀signal(7)

眼觀來說,如果你處理(異步)SIGSEGVsignalfd(2)(某些異常故障後內核產生的),它看起來像你安裝了「核心」信號處理程序,其神奇地「寫」 -s某些文件描述符上的某些字節(你幾乎可以通過在某些管道上安裝信號處理程序來模擬signalfd;但signalfd保證了一些其他方面不會有的「原子性」)。

當你從那個處理回來時,機器處於相同的狀態,所以SIGSEGV再次發生。

如果你想處理SIGSEGV你需要使用sigaction(2)或過時的signal(2)安裝一個處理程序(所以你不能使用signalfd對於SIGSEGV),然後就應該把

  • (或更多(例如,通過從安裝了sigaction(2)的信號處理程序調用siglongjmp(3))來避免從您的信號處理程序返回(例如,通過調用sigaction(2)安裝的信號處理程序)
  • 更改機器上下文(由第三個參數(a指向某個處理器的指針ucontext_t)到你的處理程序,由sigaction安裝,SA_SIGINFO)。通過更改某些寄存器或更改地址空間(例如,通過從處理程序內部調用mmap(2))。

洞察是SIGSEGV處理程序是在程序計數器設置爲錯誤機器指令的情況下輸入的。當您從SIGSEGV處理程序返回時,寄存器處於給定狀態(指針ucontext_t作爲您的sa_sigaction函數的第三個參數傳遞給sigaction)。如果你不改變這個狀態,那麼同樣的機器指令會被重新執行,並且由於你沒有改變任何東西,所以同樣的錯誤發生,同樣的SIGSEGV信號被內核重新發送。

注意:在實踐中,如果你只是想顯示回溯信息,你可以從一個SIGSEGV處理程序(例如,通過使用GCC libbacktracebacktrace(3)然後_exit(2)代替-ing從SIGSEGV信號處理程序返回的)去做;它不是完美的,並不總是工作 - 例如如果你corrupted the memory heap - 因爲你會調用非異步信號安全函數,但在實踐中運作良好。最近GCC正在這樣做(在編譯器內部,例如cc1plus及其插件),並且它有很大的幫助。

+0

謝謝。如果* a = 1生成的「SIGSEGV」不在本地線程中,那麼「SIGSEGV」屬於哪個線程? –

+0

在單線程過程中總是有一個線程(稱爲主線程)或「任務」。 –

+0

感謝您的更新。 –

2

您只能阻止kill(2)和朋友發送的SISGSEGV。從sigprocmask(2)手冊頁:

如果SIGBUS,SIGFPE,SIGILL或SIGSEGV而它們是 阻塞產生,結果是不確定的,除非通過 殺滅(2),sigqueue產生的信號(3)或提高(3)


由於信號是不是真的阻止sigprocmask - 的signalfd的先決條件 - signalfd不起作用。一個簡單的測試方法是使用你的程序,但不要導致真正的分段錯誤,發送一個信號kill -11

+0

但是'signalfd'不是*阻止*信號,所以並不真正回答這個問題... –

+0

@BasileStarynkevitch良好的通話。我編輯了我的答案,解釋說引用來自'sigprocmask'。 – cnicutar

相關問題