C 信号安全使用sem_wait()/sem_post()
我正在尝试在Linux上创建一个包装器,它控制一次允许多少并发执行。为此,我使用了一个系统范围的计数信号量。我创建信号量,执行C 信号安全使用sem_wait()/sem_post(),c,posix,C,Posix,我正在尝试在Linux上创建一个包装器,它控制一次允许多少并发执行。为此,我使用了一个系统范围的计数信号量。我创建信号量,执行sem\u wait(),启动子进程,然后在子进程终止时执行sem\u post()。那很好 问题是如何安全地处理发送到此包装器的信号。如果它没有捕获信号,则该命令可能会在不执行sem\u post()的情况下终止,从而导致信号量计数永久减少1。因此,我创建了一个信号处理程序,它执行sem\u post()。但仍然存在一个问题 如果在执行sem\u wait()之前附加处
sem\u wait()
,启动子进程,然后在子进程终止时执行sem\u post()
。那很好
问题是如何安全地处理发送到此包装器的信号。如果它没有捕获信号,则该命令可能会在不执行sem\u post()
的情况下终止,从而导致信号量计数永久减少1。因此,我创建了一个信号处理程序,它执行sem\u post()
。但仍然存在一个问题
如果在执行sem\u wait()
之前附加处理程序,则信号可能在sem\u wait()
完成之前到达,导致在没有sem\u wait()
的情况下发生sem\u post()
。如果在设置信号处理程序之前执行sem\u wait()
,则可能出现相反的情况
显然,下一步是在设置处理程序和sem\u wait()
期间阻止信号。这是我现在拥有的伪代码:
void handler(int sig)
{
sem_post(sem);
exit(1);
}
...
sigprocmask(...); /* Block signals */
sigaction(...); /* Set signal handler */
sem_wait(sem);
sigprocmask(...); /* Unblock signals */
RunChild();
sem_post(sem);
exit(0);
现在的问题是,sem\u wait()
会阻塞,在此期间,信号会被阻塞。试图终止进程的用户最终可能会求助于“kill-9”,这是我不想鼓励的行为,因为我无论如何都无法处理这种情况。我可以使用sem\u trywait()
一小段时间,然后测试sigpunding()
,但这会影响公平性,因为不再保证等待信号量最长的进程将在下一个运行
这里是否有一个真正安全的解决方案,允许我在信号量采集期间处理信号?我正在考虑使用“我是否拥有信号量”全局设置并移除信号阻塞,但这不是100%安全的,因为获取信号量并设置全局设置不是原子的,但可能比在等待时阻塞信号要好。你确定
sem_wait()
会导致信号阻塞吗?我认为情况并非如此。表示如果被信号中断,则从sem_wait()
返回EINTR
错误代码
您应该能够处理此错误代码,然后您的信号将被接收。您是否遇到过未接收到信号的情况
我会确保您处理
sem\u wait()
可以返回的错误代码。虽然这可能很少见,但如果你想100%确定你想要覆盖100%的基础。你确定你正确处理了这个问题吗?如果要等待子进程终止,可能需要使用waitpid()
系统调用。正如您所观察到的,如果孩子可能接收到信号,那么期望孩子做sem\u post()
是不可靠的。我知道这很古老,但对于那些仍然阅读谷歌提供的这篇文章的人来说
对于这个问题,最简单(也是唯一?)的健壮解决方案是使用System V信号量,它允许客户端以内核自动返回的方式获取信号量资源,无论进程如何退出。如果我不清楚,很抱歉。sem_wait()不会像您所说的那样导致信号被阻塞,但我正在使用sigprocmask()来阻止它们。但是,我认为您有一个正确的解决方案,那就是查看EINTR错误代码,这意味着处理程序不应该退出,而应该设置一些标志来表示“退出时间”。我来测试一下。谢谢,这很有效。我移除了对信号的阻塞,并将信号处理程序更改为只设置一个全局值,这意味着是时候退出了。如果sem_wait()返回错误,那么我退出,这很好,并保留信号量计数。如果我在等孩子,我也会测试退出global的时间。如果是时候退出了,我会杀死这个孩子并执行一个sem_post()来保存信号量计数。谢谢这应该是信号安全的,忽略kill-9。实际上,子级不知道信号量,父级在伪代码中的RunChild()函数中执行waitpid(),我没有显示。这一部分很好,它正在处理孩子的终止以及在sem_take()完成后发送给家长的信号。我担心设置信号处理程序和sem_wait()之间的关键部分,但检查sem_wait()错误代码是我看不到的简单解决方案。