我已经继续搜索并没有找到额外的信息,所以我只能得出结论:在POSIX中通常没有保证。
在Linux下,如果我正确理解下面的代码,只能处理一个信号(假设信号处理程序本身不会取消屏蔽信号):相关代码和一个揭示性的注释位于fs/select.c中的do_pselect
函数中:
ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
if (ret == -ERESTARTNOHAND) {
if (sigmask) {
memcpy(¤t->saved_sigmask, &sigsaved,
sizeof(sigsaved));
set_restore_sigmask();
}
} else ...
它基本上从系统调用返回,允许信号处理程序执行,之后原始信号掩码将立即恢复(来自current->saved_sigmask
,因为set_restore_sigmask()
设置了一个标志,指示应该发生这种情况)。
以下测试程序验证了这一点:
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
volatile sig_atomic_t got_usr1 = 0;
volatile sig_atomic_t got_usr2 = 0;
void handle_usr1(int signo, siginfo_t *info, void *v)
{
got_usr1 = 1;
}
void handle_usr2(int signo, siginfo_t *info, void *v)
{
got_usr2 = 1;
}
int main(int argc, char **argv)
{
sigset_t curmask;
sigemptyset(&curmask);
sigaddset(&curmask, SIGUSR1);
sigaddset(&curmask, SIGUSR2);
sigprocmask(SIG_SETMASK, &curmask, NULL);
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigdelset(&mask, SIGUSR2);
struct sigaction action;
action.sa_sigaction = handle_usr1;
sigfillset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &action, NULL);
action.sa_sigaction = handle_usr2;
sigaction(SIGUSR2, &action, NULL);
raise(SIGUSR1);
raise(SIGUSR2);
pselect(0, NULL, NULL, NULL, NULL, &mask);
int count = got_usr1 + got_usr2;
printf("Handled %d signals while in pselect.\n", count);
return 0;
}
在Linux上,以上代码的输出结果始终如一:
在pselect中处理了1个信号。
这在FreeBSD上似乎也是这样,但我不想指望所有其他平台都是这种情况。我找到的确保只能处理一个信号的解决方案是使用siglongjmp
跳出信号处理程序以及pselect
调用,同时恢复信号掩码,以便不再处理任何其他信号。
基本上,代码看起来像这样:
jmp_buf jbuf; // signal handlers have access to this
if (sigsetjmp(jbuf, 1) != 0) {
// We received a signal while in pselect ...
}
int r = pselect(nfds, &read_set_c, &write_set_c, &err_set, wait_ts, &sigmask);
信号处理程序必须执行
siglongjmp
:
void signal_handler(int signo, siginfo_t *siginfo, void *v)
{
siglongjmp(jbuf, 1);
}
这感觉有些老旧,但似乎在我测试过的所有平台上都可以工作(Linux、MacOS 和 FreeBSD) - 此外,它似乎受到 POSIX 的普遍支持。
siglongjmp
,那么在pselect
执行时肯定会排除其他信号被接收到(即手动强制执行“只有一个”限制)。实际上,这就是我最终使用的解决方案,以确保我在pselect
中仅检测到一个信号。如果您感兴趣,可以在此处查看代码:https://github.com/davmac314/dasynq/blob/master/dasynq-pselect.h - davmac