簡體   English   中英

盡管不在 sa_mask 中,但 POSIX 信號在信號處理程序中被阻塞

[英]POSIX signal being blocked in signal handler despite not being in sa_mask

我昨天發布了一個類似的問題,但我在概述我的問題方面做得很差,從那時起我認為我取得了進展。

我的最小工作示例仍然很長,因此我將發布相關片段,但可以在此處找到完整示例。

我的問題很簡單,我有兩個 POSIX 消息隊列,它們被創建為異步的,並且都由同一個線程上的同一個處理程序處理。 我的問題是在更基本的層面上,如果一個單獨的線程按順序發送到兩個隊列,那么 sig 處理程序只為第一個隊列運行一次。 根據GNU 的說法,這是有道理的,因為當信號調用處理程序時,它會自動被阻止。

因此,當我配置struct sigaction我確保從我設置為sa_masksigset_t中刪除目標信號(SIGIO)。 我的假設是,然后使用SA_NODEFER ,如sigaction(2)中所述,信號處理程序將能夠被遞歸調用(不確定 recursively 在這里是否正確)。

sa_mask 指定在信號處理程序執行期間應該被阻塞的信號掩碼(即,添加到調用信號處理程序的線程的信號掩碼中)。 此外,觸發處理程序的信號將被阻塞,除非使用 SA_NODEFER 標志。

將信號處理程序附加到消息隊列的相關代碼

assert((conn->fd = mq_open(conn->name, O_CREAT | O_RDONLY | O_NONBLOCK,
               0644, &attr)));

/** Setup handler for SIGIO */
/** sigaction(2) specifies that the triggering signal is blocked in the handler  */
/**     unless SA_NODEFER is specified */
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER; 
sa.sa_sigaction = sigHandler;
/** sa_mask specifies signals that will be blocked in the thread the signal  */
/**     handler executes in */
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGIO);
if (sigaction(SIGIO, &sa, NULL)) {
    printf("Sigaction failed\n");
    goto error;
}

printf("Handler set in PID: %d for TID: %d\n", getpid(), gettid());

/** fcntl(2) - FN_SETOWN_EX is used to target SIGIO and SIGURG signals to a  */
/**     particular thread */
struct f_owner_ex cur_tid = { .type = F_OWNER_TID, .pid = gettid() };
assert(-1 != fcntl(conn->fd, F_SETOWN_EX, &cur_tid));

作為完整性檢查,我檢查了處理程序內的信號掩碼以檢查 SIGIO 是否被阻止。

void sigHandler(int signal, siginfo_t *info, void *context)
{
    sigset_t sigs;
    sigemptyset(&sigs);
    pthread_sigmask(0, NULL, &sigs);
    if (sigismember(&sigs, SIGIO)) {
        printf("SIGIO being blocked in handler\n");
        sigaddset(&sigs, SIGIO);
        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
    }
...
}

但是 SIGIO 似乎沒有被阻止。 我的推理告訴我,鑒於兩個消息隊列 MQ1 和 MQ2 在 SIGIO 上異步使用相同的處理程序,應該會發生以下情況。 鑒於兩個線程的時間和信號的延遲,我很難真正知道。 最好說我的一些有根據的猜測是:

  • mq_send到MQ1直接跟着mq_send從線程1至MQ2
  • MQ1 的信號處理程序應該在線程 2 上給定來自 MQ1 的 SIGIO
  • MQ2 的信號處理程序會在線程 2 上中斷 MQ1 的信號處理程序
  • MQ2 的信號處理程序在線程 2 上完成
  • MQ1 的信號處理程序在線程 2 上完成

運行我之前鏈接的示例,觀察到以下行為

  • mq_send到MQ1直接跟着mq_send從線程1至MQ2
  • MQ1 的信號處理程序觸發並完成

這讓我認為 SIGIO 在信號處理過程中以某種方式被阻止或忽略。 鑒於我所讀到的sa_mask和我使用pthread_sigmask健全性檢查,我不確定為什么我會得到我所看到的行為。 我希望我在聯機幫助頁中的某處遺漏了一些小知識。

我的問題是在更基本的層面上,如果一個單獨的線程按順序發送到兩個隊列,那么 sig 處理程序只為第一個隊列運行一次......這讓我認為在信號處理程序期間SIGIO以某種方式被阻塞或忽略.

SIGIO是一種標准信號,而不是實時信號。 來自POSIX 信號概念

在信號生成與其傳遞或接受之間的時間期間,該信號被稱為“待處理”。 通常,應用程序無法檢測到此間隔。

...

如果生成了后續出現的未決信號,則在除需要排隊的情況之外的情況下,信號是否被多次傳遞或接受是實現定義的。 SIGRTMINSIGRTMAX范圍之外的多個同時掛起的信號被傳遞到進程或被進程接受的SIGRTMAX是未指定的。

在 Linux 上,標准信號不排隊,而是在一個已經掛起時被丟棄。 來自Linux man signal(7)

標准信號的排隊和傳遞語義

如果一個進程有多個標准信號掛起,則信號的傳遞順序是未指定的。

標准信號不排隊。 如果在該信號被阻塞時生成了一個標准信號的多個實例,那么只有一個信號實例被標記為未決(並且該信號在被解除阻塞時只會傳遞一次)。 在標准信號已經掛起的情況下,與該信號關聯的siginfo_t結構(請參閱 sigaction(2))不會在同一信號的后續實例到達時被覆蓋。 因此,該進程將接收與該信號的第一個實例相關聯的信息。


您的問題的一種解決方法是使用SIGEV_THREAD通知而不是SIGEV_SIGNAL ,以便您的回調被另一個線程調用。 這也消除了只能調用異步信號安全函數的信號處理程序的限制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM