在库中的write(2)调用上禁用SIGPIPE信号

时间:2016-12-16 18:14:43

标签: c linux pipe signals sigpipe

问题

当写入SIGPIPE FD,而不安装我自己的信号处理程序或全局禁用/屏蔽信号时,是否可以禁用引发信号(pipe())?

背景

我正在处理偶尔会创建管道的小型库,而fork()是一个临时的子/虚拟进程,它等待来自父级的消息。当子进程收到来自父进程的消息时,它就会(故意)死掉。

问题

对于我无法控制的情况,子进程运行来自另一个(第三方)库的代码,这些代码很容易崩溃,所以我不能总是确定子进程在我write()到管道。

这导致我有时尝试write()管道,子进程'end已经死/关闭,并在父进程中引发SIGPIPE。我在其他客户将使用的库中,因此我的库必须尽可能独立且对调用应用程序透明。安装自定义信号处理程序可能会破坏客户的代码。

到目前为止工作

我使用setsockopt(..., MSG_NOSIGNAL)解决了套接字这个问题,但我找不到任何功能上与管道相同的东西。我已经看过暂时安装一个信号处理程序来捕获SIGPIPE,但我没有看到任何方法将其范围限制在我的库中的调用函数而不是整个过程(并且它不是原子的)。

I've also found a similar question here on SO that is asking the same thing,但不幸的是,使用poll() / select()不会是原子的,并且子进程在我select()之间死亡的可能性很小(但可能)和write()来电。

问题(redux)

有没有办法完成我在这里尝试的内容,或者以原子方式检查并写入管道而不会触发将生成SIGPIPE的行为?此外,是否有可能实现知道子进程是否崩溃?知道它是否崩溃让我为供应“崩溃”库的供应商构建一个案例,让他们知道它失败的频率。

3 个答案:

答案 0 :(得分:1)

  

写入SIGPIPE FD [...]时是否可以禁用引发信号(pipe())?

父进程可以保持管道读取端的副本打开。然后总会有一个读者,即使它实际上没有读过,所以永远不会满足SIGPIPE的条件。

问题在于它是一个死锁风险。如果孩子死了并且父母后来执行了无法容纳在管道缓冲区中的阻止写入,那么你就是干杯。什么都不会从管道中读取任何空间,因此写入永远不会完成。首先避免出现此问题是SIGPIPE的目的之一。

您还可以通过waitpid()选项WNOHANG来测试孩子在您尝试写作之前是否还活着。但这引入了竞争条件,因为孩子可能会在waitpid()和写作之间死亡。

但是,如果您的写入量一直很小,并且如果您从孩子那里得到足够的反馈以确信管道缓冲区没有备份,那么您可以将这两者结合起来形成一个合理可行的系统。 / p>

答案 1 :(得分:1)

在经历了解决这个问题的所有可能方法之后,我发现只有两个场所可以解决这个问题:

  • 使用socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)代替管道。
  • 通过fork()创建一个“牺牲”子流程,如果SIGPIPE被提升,则允许该流程崩溃。

我走了socketpair路线。我不想,因为它涉及重写一些管道逻辑,但它并没有太痛苦。

谢谢!

答案 2 :(得分:0)

我不确定是否遵循:是父进程,即写入管道。您这样做是为了在一段时间后发送消息。子进程以某种方式解释消息,执行它必须执行的操作并退出。你还必须等待它,你不能先准备好消息,然后产生一个孩子来处理它。同样只是发送信号也不会起作用,因为孩子必须真正对消息的内容采取行动,而不仅仅是"做它"调用

首先想到的是你不会关闭父级管道的读取端。这样你就可以自由地写入管道,同时不会损害孩子的阅读能力。

如果不是这样,请详细说明问题。