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

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

背景

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

问题

对于我无法控制的情况,子进程运行来自易于崩溃的另一个(第三方)库的代码,因此在写入()管道之前,我不能总是确定子进程是活动的.

这导致我有时尝试写入()管道,子进程’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的行为?此外,是否有可能实现这一点,并知道子进程是否崩溃?知道它是否崩溃让我为供应“崩溃”库的供应商构建一个案例,让他们知道它失败的频率.

最佳答案

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

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

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

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

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

点赞