[英]POSIX signal being blocked in signal handler despite not being in sa_mask
我昨天發布了一個類似的問題,但我在概述我的問題方面做得很差,從那時起我認為我取得了進展。
我的最小工作示例仍然很長,因此我將發布相關片段,但可以在此處找到完整示例。
我的問題很簡單,我有兩個 POSIX 消息隊列,它們被創建為異步的,並且都由同一個線程上的同一個處理程序處理。 我的問題是在更基本的層面上,如果一個單獨的線程按順序發送到兩個隊列,那么 sig 處理程序只為第一個隊列運行一次。 根據GNU 的說法,這是有道理的,因為當信號調用處理程序時,它會自動被阻止。
因此,當我配置struct sigaction
我確保從我設置為sa_mask
的sigset_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運行我之前鏈接的示例,觀察到以下行為
mq_send
到MQ1直接跟着mq_send
從線程1至MQ2這讓我認為 SIGIO 在信號處理過程中以某種方式被阻止或忽略。 鑒於我所讀到的sa_mask
和我使用pthread_sigmask
健全性檢查,我不確定為什么我會得到我所看到的行為。 我希望我在聯機幫助頁中的某處遺漏了一些小知識。
我的問題是在更基本的層面上,如果一個單獨的線程按順序發送到兩個隊列,那么 sig 處理程序只為第一個隊列運行一次......這讓我認為在信號處理程序期間
SIGIO
以某種方式被阻塞或忽略.
SIGIO
是一種標准信號,而不是實時信號。 來自POSIX 信號概念:
在信號生成與其傳遞或接受之間的時間期間,該信號被稱為“待處理”。 通常,應用程序無法檢測到此間隔。
...
如果生成了后續出現的未決信號,則在除需要排隊的情況之外的情況下,信號是否被多次傳遞或接受是實現定義的。
SIGRTMIN
到SIGRTMAX
范圍之外的多個同時掛起的信號被傳遞到進程或被進程接受的SIGRTMAX
是未指定的。
在 Linux 上,標准信號不排隊,而是在一個已經掛起時被丟棄。 來自Linux man signal(7)
:
標准信號的排隊和傳遞語義
如果一個進程有多個標准信號掛起,則信號的傳遞順序是未指定的。
標准信號不排隊。 如果在該信號被阻塞時生成了一個標准信號的多個實例,那么只有一個信號實例被標記為未決(並且該信號在被解除阻塞時只會傳遞一次)。 在標准信號已經掛起的情況下,與該信號關聯的
siginfo_t
結構(請參閱 sigaction(2))不會在同一信號的后續實例到達時被覆蓋。 因此,該進程將接收與該信號的第一個實例相關聯的信息。
您的問題的一種解決方法是使用SIGEV_THREAD
通知而不是SIGEV_SIGNAL
,以便您的回調被另一個線程調用。 這也消除了只能調用異步信號安全函數的信號處理程序的限制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.