3

假设我有一个标志来指示我用信号启用的退出条件。然后我可以将以下处理程序附加到 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将被调用并无限期阻塞。我怎样才能避免这种情况并确保我总是能够用我的信号终止循环?

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

4

2 回答 2

2

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

于 2013-10-31T12:26:43.063 回答
1

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

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

  1. 解锁信号
  2. 称呼accept()
  3. accept()返回时再次阻塞信号

因此,您基本上可以保证该信号真正被传递和处理的唯一时间是在pselect()运行时,并且您不必担心它在您检查之后finished但在您调用之前被捕获accept()。换句话说,您确保无论何时传递该信号,它总是会中断pselect()并设置errnoEINTR,因此这是您必须检查它的唯一地方。

于 2014-12-06T02:30:52.957 回答