关于 c :ZeroMQ: 如何对 EINTR 上的不同信号类型做出反应
ZeroMQ: How to react on different signal types on EINTR
注意:这个问题与我的问题相当接近,但我至少可以对提供的解决方案使用一些工作示例,也许
目前我对阻塞
|
1
2 3 4 5 6 7 8 |
try {
zmq::poll(&items, number, timeout); } catch (zmq::error_t &ex) { if (ex.num() != EINTR) { throw; } } ... |
我的意图是:重新抛出所有捕获的异常,但那些由中断的系统调用触发的异常,我通常可以忽略(例如
如果是
目前我最好的选择是安装一个监听
相关讨论
- 我浏览了代码,除了 z/OS 操作系统之外,ZMQ 似乎并没有真正捕捉到信号。
EINTR 是errno 的值之一,表示底层系统调用失败(并且可能会重试。)NEWS 文件中的注释似乎表明 ZMQ 停止捕获信号,因为他们无法做到以一种与自己期望捕获SIGINT 的其他软件兼容的方式。 -
顺便说一句——快速测试:安装
SIGINT 处理程序时检查signal() 的返回值是SIG_DFL 还是SIG_IGN 。如果是这样,这强烈表明 ZMQ 没有安装自己的处理程序。 - 请问在这种情况下,信号的来源是什么?信号是进程或线程之间通信的一种方式。 ZeroMQ 也是一种在进程或线程之间进行通信的方式。坚持一种方式要干净得多,否则你会遇到这样的问题。如果信号是你的,那么我会用 ZMQ 套接字替换它们。如果信号的来源是外部的且不可避免,那是不幸的; @ScottM 的建议听起来很明智。
-
@bazza:信号类型错误;在这种情况下,这是一个超载的术语。 OP 表示 Unix 信号,即异常情况,例如
SIGNINT 、SIGSEGV 等。这些不用于跨线程通信。 - @ScottM我知道OP指的是Unix信号,它们用于信息通信。 SIGINT 和大多数其他信号的含义只有在您不安装处理程序的情况下才具有非任意性。而且,您是否建议您不能使用特定于线程的信号掩码?无论如何,使用单独的 ZMQ 套接字作为命令通道,而不是使用信号(例如 SIGINT)在 ZMQ 的 Actor 模型框架之上施加破坏性命令基础设施要容易得多。 OP 正在尝试(不必要地)将 SIGINT 用于此目的。
-
@bazza:我不是试图使用信号作为一种通信方式,但我想对
CTRL-C 做出适当的反应:在这种情况下EINTR 表示"中止",在所有其他情况下我想忽略它并重新启动中止命令(在我的情况下为zmq::poll )。所以换句话说:我只想对CTRL-C 做出反应,最好不必安装信号处理程序(因为我正在研究一个库并且不想修改一般的信号处理策略) - 你好@frans,如果你的库没有控制信号处理策略,那么你需要应用程序不屏蔽 SIGINT。您仍然会限制应用程序对信号的使用。此外,您必须在代码中的任何地方(zmq_recv、zmq_send 等)处理 EINTR,而不仅仅是在 zmq_poll 上(信号是异步的,它们不会等待您对 zmq_poll 的调用)。如果您的"中止"命令是通过包含在被轮询的套接字集中的 ZMQ 套接字传递的消息,那就更好了。您也可以在投票中包含 stdin 以在按键时中止。
-
@frans,@bazza:您将遇到的主要问题是从信号处理程序中抛出异常。
EINTR 只是表示系统调用被中断。这并不一定意味着执行了signal 处理程序——您仍然必须捕获SIGINT ,可能会调整某些状态(希望是原子的),然后引发 C 异常。不建议在signal 处理程序中引发异常,并且非常未定义。基本上,您的方法不太可能可行。 -
@frans:不要尝试从 Unix 或 Linux
signal 处理程序内部引发 C 异常。严重地。您可以在 Windows 上执行此操作,因为结构化异常处理程序 (SEH) 机制旨在执行此操作。 Unixsignal 不是。 - @ScottM,frans 根本不想使用信号处理程序(请参阅上面 frans 的最新评论),我主张完全避免使用信号来达到所需的结果。 Frans 的代码片段不在信号处理程序中,它位于循环的顶部。但是,是的,一般来说,从信号处理程序中抛出东西并不是一个好主意。
-
@bazza:我意识到@frans 想要避免不可避免的情况,但对于
SIGINT 处理来说却无法避免。我的建议(警告)是不要尝试从信号处理程序内部抛出异常。 ZMQ 在这个用例中没有帮助。 @fran 的另一个问题是signal 与 C 异常混为一谈;它们是两种截然不同且不兼容的功能。 -
@frans:它看起来不像
libzmq 捕获SIGINT ,所以你当前的解决方案看起来是最好的解决方案。
如果我正确阅读了原始发帖者的问题,@frans 会询问是否有办法重新抛出某些异常,其中 C 异常包含
提出了两个不同的问题,关于 POSIX
- POSIX 信号与 C 异常处理无关。
-
zmq::error_t 是由zmq::poll() 作为返回EINTR 的系统调用的结果生成的 C 异常。
TL;DR 回答:不,没有更清洁的方法。
POSIX/Unix/Linux/BSD
其他警告:不要从 POSIX/Unix/Linux/BSD 信号处理程序中抛出 C 异常。这在此 SO 主题中之前已讨论过。
我遇到了一个类似的问题,并且盯着这个问题及其答案看了很长时间,希望如果我足够努力的话,一个解决方案会神奇地出现。
最后,我采用了本文中详述的方法。
底线是我们需要一个基于
这实际上是在轮询器中添加一个"信号outlets"。轮询器将返回(使用
现在,在您的 poll 调用之后,当您通常通过现在可读/可写的套接字时,您首先检查您从信号处理程序中设置的标志。信号在
一些注意事项:为此,您不能使用任何其他阻塞调用,因此您所有的
另一件事是您已经提到的:在库的情况下,用户应该可以自由安装自己的信号处理程序。因此,如果他们想以稳健的方式使用您的库,您可能需要指导用户如何以这种方式正确处理信号。我认为如果您向用户公开标志(或翻转标志的函数)以便他们可以从自己的信号处理程序中调用它应该是可行的。
好的,所以有三种方法,一种简单,两种麻烦
干净
简单的方法是这样运行的:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
zmq::socket_t inSock;
zmq::pollitem_t pollOnThese[2]; int quitLoop = 0; <code to connect inSock to something> pollOnThese[0].socket = NULL; // This item is not polling a ZMQ socket pollOnThese[1].socket = &inSock; // This item polls inSock while (!quitLoop) if (pollOnThese[0].revents == ZMQ_POLLIN) if (c == 'c') |
当然这意味着你的"abort"不再是用户按下 CTRL-C,他们只是按下键 \\'c\\',或者实际上任何可以写入这个标准输入的程序都可以发送一个 \\'c \\'。或者,您也可以添加另一个 ZMQ 套接字作为命令通道。这里根本没有对信号的容忍度,但我一直发现将信号的使用与 Actor 模型编程混合使用是一件非常尴尬的事情。见下文。
凌乱
使用信号的混乱方式看起来像这样:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
zmq::socket_t inSock;
zmq::pollitem_t pollOnThis; int quitLoop = 0; <code to connect inSock to something> <install a signal handler that handles SIGINT by setting quitLoop to 1> pollOnThis.socket = &inSock; // This item polls inSock while (!quitLoop) if (pollOnThis.revent == ZMQ_POLLIN && !quitLoop) <repeat this loop for every single zmq:: recv and zmq::send> |
混合(也很混乱)
在此处的 ZeroMQ 指南文档中,他们通过让信号处理程序写入管道并将管道包含在 zmq_poll 中,就像我在上面使用标准输入所做的那样,它们有点融合了这两种想法。
这是将异步信号转换为同步事件的常用技巧。
但是根本没有提示是否可以重新启动任何 zmq 例程。他们只是将其用作启动干净关闭的一种方式,已放弃任何正在进行的 recv() 或 send()。如果为了彻底中止,您需要完成一系列 recv() 和 send() 例程,那么如果正在使用信号,则无法保证这是可能的。
如果我错了,请原谅我,但感觉就像你正在重新利用 SIGINT 的意义,而不是"立即终止整个程序"。如果是这样,您可能希望稍后恢复您的通信循环。在这种情况下,谁知道在您的信号到达后是否有任何 recv() 或 send() 调用可以恢复。 zmq 将使用一大堆系统调用。它们中的许多在某些情况下是不可重新启动的,并且不知道 ZMQ 是如何使用这些调用的(缺少阅读它们的源代码)。
结论
基本上我会完全避免使用信号,尤其是当您不想安装自己的处理程序时。通过使用标准输入或管道或另一个 ZMQ 套接字(或实际上所有三个)作为包含在 zmq_poll() 中的"中止"通道,您将提供一种简单有效的方法来中止循环,并且不会产生任何并发症从它的使用。