【问题标题】:how does pselect blocks signal using signal mask in network programmingpselect 如何在网络编程中使用信号掩码阻止信号
【发布时间】:2017-09-04 19:01:35
【问题描述】:

我目前正在研究网络编程的概念,其中我遇到了解决 select 问题的函数 pselect() 之一。使用 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 返回时,进程的信号掩码会重置为调用 pselect 之前的值(即阻塞 SIGINT)。

但是在上面提到的带有 pselect 的代码中,我们阻塞了信号,那么我们如何检查错误 EINTR?由于 pselect 会阻止所有信号,因此当发生中断时,它应该阻止该信号中断或传递给进程。只有当 pselect 返回时,才能传递信号。

根据前面提到的注释here前面的行,在调用 pselect 之前或在第一次检查和 pselect 之间或调用 pselect 时仍然可能发生中断信号,这与阻塞中断和任何其他信号,因此应该导致与 select 存在竞争条件。

请任何人解释这是怎么可能的,因为我是这些概念的新手。

【问题讨论】:

    标签: sockets unix network-programming multiplexing


    【解决方案1】:

    嗯,主要思想是做ready = pselect(nfds, &amp;readfds, &amp;writefds, &amp;exceptfds, timeout, &amp;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()可以被SIGINT打断。 在pselect() 返回时,我们将不会再收到任何进一步的SIGINT,但如果SIGINTpselect() 期间发生,那么我们将根据errno 检测到这一点EINTR,如果我们检查intr_flag,我们会发现它是1。我们会明白,我们需要采取相应的行动。因此,很明显,信号处理程序可以在信号解除阻塞后立即完成其工作,而后者发生在 pselect() 调用本身内。

    这里最重要的细节是如果我们没有一个特殊的pselect()调用以原子方式实现,那么我们必须围绕@执行步骤987654354@标签在sn-p上面我们自己使用时通常使用select()。而且,由于它不会是原子的,我们将有机会在两个操作之间将 SIGINT 传递给我们 - /* NB: NOTE-1 */ 提示所在的位置,即在我们取消阻止信号从被传递到输入select() 之前。到那时,信号确实会丢失。这就是为什么我们需要一个 pselect() 来代替。

    总而言之,pselect()原子性就是对其使用的解释。 如果您对此类概念不太熟悉,可以参考维基百科上的article 或计算机科学主题的专门书籍。

    另外,我将通过 LWN 上的 article 链接提供我的答案,该链接更加详尽。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      相关资源
      最近更新 更多