2015-06-08 19 views
1

如何取消發送的信號到尚未交付的進程?如何取消未決信號?

考慮一下我向某個進程發送了一個信號但該進程處於不可中斷狀態的場景。

我正在做一個有條件的等待信號來處理。但是因爲我不想繼續執行。在這種情況下,是否有辦法可以取消發送的信號(尚未傳送)

+0

這豈不是可以使用超時功能作爲一個變通? –

+1

你爲什麼要這樣做?一旦發送信號,它將被異步發送。如果目標進程終止或丟棄信號,它將被忽略。只需在目標進程中進行檢查以檢查信號是否仍然有必要。 –

+0

@LieRyan這種情況通常是在收集所有線程的bt時。所以,它實際上是一個非常通用的代碼。所有線程都無法處理。 https://android.googlesource.com/platform/system/core/+/master/libbacktrace/BacktraceCurrent.cpp#142 –

回答

4

如果在信號傳送之前該信號被忽略,則掛起的信號將被取消。你只需要忽略信號。您可以通過sigaction()sa_handler字段設置爲struct sigactionSIG_IGN來完成此操作。

下面是一些示例代碼,說明了這一點,並顯示它的工作原理。該代碼執行以下操作:

  1. SIGINT使我們擁有的時間窗口,以SIGINT發送它時,它被阻塞 - 這將產生一個懸而未決SIGINT當過程信號屏蔽變更爲將要交付不包括SIGINT(並且如果信號被忽略,將被取消)的掩碼。
  2. 等待你知道一個SIGINT掛起後發送SIGINT
  3. 忽略SIGINT。這具有取消未決信號的效果。
  4. 恢復原始默認動作SIGINT,即終止進程
  5. 恢復原來的進程信號掩碼,它不會阻止SIGINT
  6. 等待您輸入任何字符來終止。

您可以看到,進程在步驟5之後沒有終止並等待輸入,這意味着掛起的信號被取消。

下面是說明了這個代碼:

#include <signal.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(void) { 

    sigset_t block_sigint, prev_mask; 
    sigemptyset(&block_sigint); 
    sigaddset(&block_sigint, SIGINT); 

    if (sigprocmask(SIG_SETMASK, &block_sigint, &prev_mask) < 0) { 
     perror("Couldn't block SIGINT"); 
     return 0; 
    } 

    printf("SIGINT blocked: kill -SIGINT %ld to generate a pending SIGINT. Press return when done.\n", (long) getpid()); 

    /* Now, open a new terminal and send SIGINT to this process. 
    * 
    * After doing that, the signal will be pending delivery because it is currently blocked. 
    * 
    * Now, if we ignore SIGINT, the pending signal will be cancelled 
    */ 

    getchar(); 

    struct sigaction ign_sigint, prev; 
    ign_sigint.sa_handler = SIG_IGN; 
    ign_sigint.sa_flags = 0; 
    sigemptyset(&ign_sigint.sa_mask); 

    if (sigaction(SIGINT, &ign_sigint, &prev) < 0) { 
     perror("Couldn't ignore SIGINT"); 
     return 0; 
    } 

    printf("SIGINT ignored - pending SIGINT was canceled.\n"); 

    /* Now, restore the default action for SIGINT */ 
    if (sigaction(SIGINT, &prev, NULL) < 0) { 
     perror("Couldn't restore default SIGINT behavior"); 
     return 0; 
    } 

    /* And also restore the process's original sigmask, which does not block SIGINT */ 
    if (sigprocmask(SIG_SETMASK, &prev_mask, NULL) < 0) { 
     perror("Couldn't restore original process sigmask"); 
     return 0; 
    } 

    printf("Process sigmask and action for SIGINT are now restored\n"); 

    /* We will not receive SIGINT at this point because it was canceled 
    * So the process will block on getchar() here instead of terminating 
    */ 
    getchar(); 

    return 0; 
}