pselect如何在网络编程中使用信号掩码来阻止信号
how does pselect blocks signal using signal mask in network programming
我目前正在研究网络编程的概念,其中我遇到了一个函数 pselect() ,它解决了 select 的问题,即。使用select(),有可能出现问题,即在intr_flag的测试和对select的调用之间,如果信号出现,如果select 永远阻塞。
if (intr_flag)
handle_intr(); /* handle the signal */
if ( (nready = select( ... )) < 0) {
if (errno == EINTR) {
if (intr_flag)
handle_intr();
}
但是,它说有了 pselect,我们现在可以可靠地将此示例编码为
sigset_t newmask, oldmask, zeromask;
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */
if (intr_flag) //here
handle_intr(); /* handle the signal */
if ( (nready = pselect ( ... , &zeromask)) < 0) {
if (errno == EINTR) { //here
if (intr_flag)
handle_intr ();
}
...
}
它给出的代码可靠的解释是 - 在测试 intr_flag 变量之前,我们阻止 SIGINT。当调用 pselect 时,它用空集(即 zeromask)替换进程的信号掩码,然后检查描述符,可能会进入休眠状态。但是当 pselect returns 时,进程的信号掩码被重置为调用 pselect 之前的值(即,SIGINT 被阻塞)。
但是在上面提到的pselect的代码中,我们屏蔽了信号,那么我们如何检查错误EINTR呢?由于 pselect 阻止所有信号,因此当中断发生时,它应该阻止中断或被传递到进程。只有当pselect returns时才可以传递信号。
根据前面提到的评论这里的行,中断信号仍然可以在调用 pselect 之前或在第一次检查和pselect 或当 pselect 被调用时与阻止中断和任何其他信号的目的相矛盾,因此应该导致竞争条件,因为 select 在那里。
请任何人解释这是怎么可能的,因为我是这些概念的新手。
嗯,主要思想是 ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
等同于原子地执行以下操作:
sigset_t sigsaved;
sigprocmask(SIG_SETMASK, &sigmask, &sigsaved);
/* NB: NOTE-1 */
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
为了利用这个,我们首先阻止,比如说,SIGINT
:
sigset_t emptyset, blockset;
sigemptyset(&blockset);
sigaddset(&blockset, SIGINT);
sigprocmask(SIG_BLOCK, &blockset, NULL);
那时我们无法收到 SIGINT
,因此我们无法处理它。但是在输入 pselect()
之前我们不需要它。我们想做什么
在我们阻止之后 SIGINT
是设置一个适当的信号处理程序。
比如说,我们在主代码之外声明了一个标志 static volatile int intr_flag = 0;
,我们定义了一个名为 handler()
的处理程序,它只执行 intr_flag = 1;
。因此,我们将其设置为处理程序:
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
然后我们配置readfs
(这里声明不显示),
初始化一个空信号集并调用 pselect()
:
sigemptyset(&emptyset);
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset);
因此,我将再次概述这一点 - 在我们调用 pselect()
之前,我们不会收到 SIGINT
。我们不需要它。一旦我们输入 pselect()
,信号 SIGINT
将被解锁(因为提供给 pselect()
的空信号集),并且 pselect()
可以被 [=15= 中断].
在 pselect()
returns 时,我们将不会再收到任何进一步的 SIGINT
,但如果 在 期间发生 pselect()
,那么我们会根据 errno
检测到它是 EINTR
,如果我们检查 intr_flag
,我们会发现它是 1
。我们会明白我们需要相应地行事。所以,很明显,一旦信号被解锁,信号处理程序就可以完成它的工作,而后者发生在 pselect()
调用自身中。
这里最重要的细节是如果我们没有一个特殊的pselect()
调用以atomic方式实现, 然后我们必须在使用通常的 select()
时围绕上面代码段中的 /* NB: NOTE-1 */
标签执行步骤。而且,由于它 不会是原子的 ,我们将有机会在两个操作之间将 SIGINT
传递给我们 - 其中 /* NB: NOTE-1 */
提示是位于,即在我们取消阻止信号传输之后和输入 select()
之前。那时信号确实会丢失。这就是我们需要 pselect()
调用的原因。
总而言之,pselect()
的原子性就是对其使用的说明。
如果您对这样的概念不是很熟悉,可以参考维基百科article或计算机科学专题的专门书籍。
此外,我将在 LWN 上用 link 对 article 进行更详尽的回答。
我目前正在研究网络编程的概念,其中我遇到了一个函数 pselect() ,它解决了 select 的问题,即。使用select(),有可能出现问题,即在intr_flag的测试和对select的调用之间,如果信号出现,如果select 永远阻塞。
if (intr_flag)
handle_intr(); /* handle the signal */
if ( (nready = select( ... )) < 0) {
if (errno == EINTR) {
if (intr_flag)
handle_intr();
}
但是,它说有了 pselect,我们现在可以可靠地将此示例编码为
sigset_t newmask, oldmask, zeromask;
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */
if (intr_flag) //here
handle_intr(); /* handle the signal */
if ( (nready = pselect ( ... , &zeromask)) < 0) {
if (errno == EINTR) { //here
if (intr_flag)
handle_intr ();
}
...
}
它给出的代码可靠的解释是 - 在测试 intr_flag 变量之前,我们阻止 SIGINT。当调用 pselect 时,它用空集(即 zeromask)替换进程的信号掩码,然后检查描述符,可能会进入休眠状态。但是当 pselect returns 时,进程的信号掩码被重置为调用 pselect 之前的值(即,SIGINT 被阻塞)。
但是在上面提到的pselect的代码中,我们屏蔽了信号,那么我们如何检查错误EINTR呢?由于 pselect 阻止所有信号,因此当中断发生时,它应该阻止中断或被传递到进程。只有当pselect returns时才可以传递信号。
根据前面提到的评论这里的行,中断信号仍然可以在调用 pselect 之前或在第一次检查和pselect 或当 pselect 被调用时与阻止中断和任何其他信号的目的相矛盾,因此应该导致竞争条件,因为 select 在那里。
请任何人解释这是怎么可能的,因为我是这些概念的新手。
嗯,主要思想是 ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
等同于原子地执行以下操作:
sigset_t sigsaved;
sigprocmask(SIG_SETMASK, &sigmask, &sigsaved);
/* NB: NOTE-1 */
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
为了利用这个,我们首先阻止,比如说,SIGINT
:
sigset_t emptyset, blockset;
sigemptyset(&blockset);
sigaddset(&blockset, SIGINT);
sigprocmask(SIG_BLOCK, &blockset, NULL);
那时我们无法收到 SIGINT
,因此我们无法处理它。但是在输入 pselect()
之前我们不需要它。我们想做什么
在我们阻止之后 SIGINT
是设置一个适当的信号处理程序。
比如说,我们在主代码之外声明了一个标志 static volatile int intr_flag = 0;
,我们定义了一个名为 handler()
的处理程序,它只执行 intr_flag = 1;
。因此,我们将其设置为处理程序:
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
然后我们配置readfs
(这里声明不显示),
初始化一个空信号集并调用 pselect()
:
sigemptyset(&emptyset);
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset);
因此,我将再次概述这一点 - 在我们调用 pselect()
之前,我们不会收到 SIGINT
。我们不需要它。一旦我们输入 pselect()
,信号 SIGINT
将被解锁(因为提供给 pselect()
的空信号集),并且 pselect()
可以被 [=15= 中断].
在 pselect()
returns 时,我们将不会再收到任何进一步的 SIGINT
,但如果 在 期间发生 pselect()
,那么我们会根据 errno
检测到它是 EINTR
,如果我们检查 intr_flag
,我们会发现它是 1
。我们会明白我们需要相应地行事。所以,很明显,一旦信号被解锁,信号处理程序就可以完成它的工作,而后者发生在 pselect()
调用自身中。
这里最重要的细节是如果我们没有一个特殊的pselect()
调用以atomic方式实现, 然后我们必须在使用通常的 select()
时围绕上面代码段中的 /* NB: NOTE-1 */
标签执行步骤。而且,由于它 不会是原子的 ,我们将有机会在两个操作之间将 SIGINT
传递给我们 - 其中 /* NB: NOTE-1 */
提示是位于,即在我们取消阻止信号传输之后和输入 select()
之前。那时信号确实会丢失。这就是我们需要 pselect()
调用的原因。
总而言之,pselect()
的原子性就是对其使用的说明。
如果您对这样的概念不是很熟悉,可以参考维基百科article或计算机科学专题的专门书籍。
此外,我将在 LWN 上用 link 对 article 进行更详尽的回答。