POSIX 信号在信号处理程序中被阻止,尽管不在 sa_mask 中
POSIX signal being blocked in signal handler despite not being in sa_mask
我昨天 post 编辑了一个类似的 question,但我没有很好地概述我的问题,从那以后我认为我取得了进步。
我的最小工作示例仍然很长,因此我将 post 相关片段,但可以找到完整示例 here。
我的问题很简单,我有两个 POSIX 创建为异步的消息队列,并且都由同一线程上的同一处理程序处理。我的问题是在更基本的层面上,如果一个单独的线程顺序发送到两个队列,那么第一个队列的 sig 处理程序只有 运行 一次。根据 GNU,鉴于当信号调用处理程序时它会自动被阻止,这是有道理的。
因此,当我配置我的 struct sigaction
时,我确保从我设置为 sa_mask
的 sigset_t
中删除目标信号 (SIGIO)。我的假设是,然后使用 SA_NODEFER
,如 sigaction(2) 中所述,信号处理程序将能够被递归调用(不确定递归在这里是否是正确的词)。
sa_mask specifies a mask of signals which should be blocked (i.e.,
added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler. In
addition, the
signal which triggered the handler will be blocked, unless the
SA_NODEFER flag is used.
将信号处理器附加到消息队列的相关代码
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
进行的健全性检查,我不确定为什么我会得到我所看到的行为。我希望我错过了联机帮助页中的一些小知识。
My problem is at a more fundamental level in that if a separate thread sends to both queues sequentially then the sig handler is only run once for the first queue... Which makes me think that somehow SIGIO
is being blocked or ignored during the signal handler.
SIGIO
是标准信号,不是实时信号。
来自 POSIX Signal Concepts:
During the time between the generation of a signal and its delivery or acceptance, the signal is said to be "pending". Ordinarily, this interval cannot be detected by an application.
...
If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once in circumstances other than those in which queuing is required. The order in which multiple, simultaneously pending signals outside the range SIGRTMIN
to SIGRTMAX
are delivered to or accepted by a process is unspecified.
在 Linux 上,标准信号不排队,而是在一个已经挂起时被丢弃。来自 Linux man signal(7)
:
Queueing and delivery semantics for standard signals
If multiple standard signals are pending for a process, the order in
which the signals are delivered is unspecified.
Standard signals do not queue. If multiple instances of a standard
signal are generated while that signal is blocked, then only one
instance of the signal is marked as pending (and the signal will be
delivered just once when it is unblocked). In the case where a
standard signal is already pending, the siginfo_t
structure (see
sigaction(2)) associated with that signal is not overwritten on
arrival of subsequent instances of the same signal. Thus, the
process will receive the information associated with the first
instance of the signal.
您的问题的一个解决方法是使用 SIGEV_THREAD
通知而不是 SIGEV_SIGNAL
,这样您的回调就会被另一个线程调用。这也消除了信号处理程序的限制,您只能调用 async-signal-safe functions.
我昨天 post 编辑了一个类似的 question,但我没有很好地概述我的问题,从那以后我认为我取得了进步。
我的最小工作示例仍然很长,因此我将 post 相关片段,但可以找到完整示例 here。
我的问题很简单,我有两个 POSIX 创建为异步的消息队列,并且都由同一线程上的同一处理程序处理。我的问题是在更基本的层面上,如果一个单独的线程顺序发送到两个队列,那么第一个队列的 sig 处理程序只有 运行 一次。根据 GNU,鉴于当信号调用处理程序时它会自动被阻止,这是有道理的。
因此,当我配置我的 struct sigaction
时,我确保从我设置为 sa_mask
的 sigset_t
中删除目标信号 (SIGIO)。我的假设是,然后使用 SA_NODEFER
,如 sigaction(2) 中所述,信号处理程序将能够被递归调用(不确定递归在这里是否是正确的词)。
sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used.
将信号处理器附加到消息队列的相关代码
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
进行的健全性检查,我不确定为什么我会得到我所看到的行为。我希望我错过了联机帮助页中的一些小知识。
My problem is at a more fundamental level in that if a separate thread sends to both queues sequentially then the sig handler is only run once for the first queue... Which makes me think that somehow
SIGIO
is being blocked or ignored during the signal handler.
SIGIO
是标准信号,不是实时信号。
来自 POSIX Signal Concepts:
During the time between the generation of a signal and its delivery or acceptance, the signal is said to be "pending". Ordinarily, this interval cannot be detected by an application.
...
If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once in circumstances other than those in which queuing is required. The order in which multiple, simultaneously pending signals outside the range
SIGRTMIN
toSIGRTMAX
are delivered to or accepted by a process is unspecified.
在 Linux 上,标准信号不排队,而是在一个已经挂起时被丢弃。来自 Linux man signal(7)
:
Queueing and delivery semantics for standard signals
If multiple standard signals are pending for a process, the order in which the signals are delivered is unspecified.
Standard signals do not queue. If multiple instances of a standard signal are generated while that signal is blocked, then only one instance of the signal is marked as pending (and the signal will be delivered just once when it is unblocked). In the case where a standard signal is already pending, the
siginfo_t
structure (see sigaction(2)) associated with that signal is not overwritten on arrival of subsequent instances of the same signal. Thus, the process will receive the information associated with the first instance of the signal.
您的问题的一个解决方法是使用 SIGEV_THREAD
通知而不是 SIGEV_SIGNAL
,这样您的回调就会被另一个线程调用。这也消除了信号处理程序的限制,您只能调用 async-signal-safe functions.