假设我有一个标志来指示我要使用信号启用的退出条件。然后我可以将以下处理程序附加到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的阻塞行为),如果有一些更清晰,更简单的解决方案,我想改用它。
答案 0 :(得分:2)
在这种情况下可以使用Self-Pipe Trick。
打开管道并使用select
在pipefd和sockfd上等待。处理程序将char写入管道。选择之后,检查fd设置可以帮助您确定是否可以接受接受。
答案 1 :(得分:1)
我意识到这个问题已经过了一年多了,但是pselect()
正是针对这种情况设计的。您可以提供pselect()
(和select()
一般)侦听套接字的文件描述符,当有accept()
连接可用时,这些函数将返回。
一般方法是阻止所有相关信号,然后使用信号掩码调用pselect()
以取消阻止它们。 pselect()
将原子地:
accept()
accept()
返回所以你基本上可以保证信号实际交付和处理的唯一时间是pselect()
正在运行,并且你不必担心它会在你检查{{1}后被抓住但在你致电finished
之前。换句话说,您确保无论何时传递信号,它都会始终中断accept()
并将pselect()
设置为errno
,这是您唯一的位置必须检查它。