使用sig_atomic_t标志和阻塞调用

时间:2013-10-31 07:04:13

标签: c signals posix

假设我有一个标志来指示我要使用信号启用的退出条件。然后我可以将以下处理程序附加到SIGUSR1中。

volatile sig_atomic_t finished = 0;

void catch_signal(int sig)
{
  finished = 1;
}

然后我使用该标志来确定特定循环应该何时结束。在这种特殊情况下,我有一个线程正在运行(但我相信我的问题也适用于没有线程,因此不要专注于该部分)。

void *thread_routine(void *arg)
{
  while (!finished) {
    /* What if the signal happens here? */
    if ((clientfd = accept(sockfd, &remote_addr, &addr_size)) == -1) {
      if (errno == EINTR)
        continue;
      /* Error handling */
    }

    handle_client(clientfd);
  }
}

这个循环应该继续运行,直到我举起SIGUSR1信号。当它收到信号时,我希望它尽快优雅地停止。因为我有一个阻塞接受调用,所以没有循环旋转浪费CPU周期,这很好,信号可以随时中断阻塞接受并导致循环终止。

问题是,如代码中的注释所示,信号可以在while条件之后但在接受调用之前传递。然后信号处理程序将finished设置为true,但在执行恢复后,accept将被调用并无限期阻塞。我怎样才能避免这种情况,并确保我总能用信号终止循环?

假设我仍然想用信号来控制它,我可以想到两种可能的解决方案。第一个是打开一些警报,如果第一次错过信号,会在一段时间后重新发出信号。第二个是在套接字上设置超时,以便在一段时间后返回accept,以便再次检查该标志。但是这些解决方案更像是解决方法(特别是因为我在第二个解决方案中更改了accept的阻塞行为),如果有一些更清晰,更简单的解决方案,我想改用它。

2 个答案:

答案 0 :(得分:2)

在这种情况下可以使用Self-Pipe Trick。 打开管道并使用select在pipefd和sockfd上等待。处理程序将char写入管道。选择之后,检查fd设置可以帮助您确定是否可以接受接受。

答案 1 :(得分:1)

我意识到这个问题已经过了一年多了,但是pselect()正是针对这种情况设计的。您可以提供pselect()(和select()一般)侦听套接字的文件描述符,当有accept()连接可用时,这些函数将返回。

一般方法是阻止所有相关信号,然后使用信号掩码调用pselect()以取消阻止它们。 pselect()将原子地:

  1. 取消阻止信号
  2. 致电accept()
  3. accept()返回
  4. 时再次阻止信号

    所以你基本上可以保证信号实际交付和处理的唯一时间是pselect()正在运行,并且你不必担心它会在你检查{{1}后被抓住但在你致电finished之前。换句话说,您确保无论何时传递信号,它都会始终中断accept()并将pselect()设置为errno,这是您唯一的位置必须检查它。