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

Disable SIGPIPE signal on write(2) call in library

问题

是否可以在写入 pipe() FD 时禁用信号 (SIGPIPE) 的提升,无需安装我自己的信号处理程序或 disabling/masking全球信号?


背景

我正在开发一个偶尔会创建管道的小型库,fork() 是一个临时的 child/dummy 进程,用于等待来自父进程的消息。当子进程收到父进程的消息时,它就死了(故意)。


问题

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

这导致我有时会尝试 write() 到子进程末端已经 dead/closed 的管道,并在父进程中引发 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 的行为的情况下自动检查并写入管道?另外,是否可以实现and知道子进程是否崩溃?知道它是否崩溃让我为提供“崩溃”库的供应商建立一个案例,并让他们知道它失败的频率。

Is it possible to disable the raising of a signal (SIGPIPE) when writing to a pipe() FD [...]?

parent 进程可以保持其管道读取端的副本打开。那么总会有一个reader,即使它实际上没有读取,所以永远不会满足SIGPIPE的条件。

问题在于存在死锁风险。如果 child 死了,然后 parent 执行了管道缓冲区中无法容纳的阻塞写入,那么你就完蛋了。永远不会从管道中读取任何内容来释放任何 space,因此写入永远无法完成。避免这个问题首先是 SIGPIPE 的目的之一。

您还可以在尝试写入之前通过带有选项 WNOHANGwaitpid() 测试 child 是否仍然存在。但这引入了竞争条件,因为 child 可能会在 waitpid() 和写入之间死亡。

但是,如果您的写入始终很小,并且如果您从 child 获得足够的反馈以确信管道缓冲区没有备份,那么您可以将这两者结合起来形成一个合理的可行的系统。

不确定我是否遵循:you 是 parent 进程,即 you 写入管道。您这样做是为了在一段时间后发送消息。 child 进程以某种方式解释消息,执行它必须执行的操作并退出。您还必须等待它,您不能先准备好消息然后生成 child 来处理它。此外,仅发送信号并不能解决问题,因为 child 必须真正对消息的内容起作用,而不仅仅是 "do it" 调用。

想到的第一个技巧是您不会关闭parent中管道的读取端。这允许您自由写入管道,同时不会损害 child 从中读取的能力。

如有不妥,请详细说明。

在尝试了所有可能的方法来解决这个问题之后,我发现只有两个地方可以解决这个问题:

  • 使用 socketpair(PF_LOCAL, SOCK_STREAM, 0, fd),代替管道。
  • 通过 fork() 创建一个 "sacrificial" 子进程,如果引发 SIGPIPE 则允许崩溃。

我走了 socketpair 路线。我不想,因为它涉及重写相当多的管道逻辑,但这并不太痛苦。

谢谢!