C++ ZeroMQ:如何对EINTR上的不同信号类型做出反应
注意:与我的非常接近,但我至少可以使用一些工作示例来提供解决方案,也许C++ ZeroMQ:如何对EINTR上的不同信号类型做出反应,c++,signals,zeromq,C++,Signals,Zeromq,注意:与我的非常接近,但我至少可以使用一些工作示例来提供解决方案,也许ZeroMQ带来了一些我不知道的魔力 目前,我在阻塞ZeroMQ调用时对异常做出如下反应: try { zmq::poll(&items, number, timeout); } catch (zmq::error_t &ex) { if (ex.num() != EINTR) { throw; } } ... zmq::socket_t inSock; zmq::po
ZeroMQ
带来了一些我不知道的魔力
目前,我在阻塞ZeroMQ
调用时对异常做出如下反应:
try {
zmq::poll(&items, number, timeout);
} catch (zmq::error_t &ex) {
if (ex.num() != EINTR) {
throw;
}
}
...
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[0].fd = 0; // Poll on stdin. 0 is the fd for stdin
pollOnThese[0].event = ZMQ_POLLIN;
pollOnThese[1].socket = &inSock; // This item polls inSock
pollOnThese[1].fd = 0; // This field is ignored because socket isn't NULL
pollOnThese[1].event = ZMQ_POLLIN;
while (!quitLoop)
{
zmq::poll(pollOnThese,2);
if (pollOnThese[0].revents == ZMQ_POLLIN)
{
// A key has been pressed, read it
char c;
read(0, &c, 1);
if (c == 'c')
{
quitloop = 1;
}
}
if (pollOnThese[1].revent == ZMQ_POLLIN)
{
// Handle inSock as required
}
}
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
pollOnThis.fd = 0; // This field is ignored because socket isn't NULL
pollOnThis.event = ZMQ_POLLIN;
while (!quitLoop)
{
try
{
zmq::poll(&pollOnThis, 1);
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
}
if (pollOnThis.revent == ZMQ_POLLIN && !quitLoop)
{
// Handle inSock as required
bool keepReading = true;
do
{
try
{
inSock.recv(&message)
keepReading = false;
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
else
{
// We also may want to test quitFlag here because the signal,
// being asynchronous, may have been delivered mid recv()
// and the handler would have set quitFlag.
if (quitFlag)
{
// Abort
keepReading = false;
}
else
{
// Some other signal interrupted things
// What to do? Poll has said a message can be read without blocking.
// But does EINTR mean that that meassage has been partially read?
// Has one of the myriad of system calls that underpin recv() aborted?
// Is the recv() restartable? Documentation doesn't say.
// Have a go anyway.
keepReading = true;
}
} // if
} // catch
}
while (keepReading);
<repeat this loop for every single zmq:: recv and zmq::send>
}
}
我的意图是:重新显示所有捕获的异常,但由中断的系统调用触发的异常除外,我通常可以忽略这些异常(例如,SIGPROF
),然后重新启动zmq::poll
如果是SIGINT
(CTRL-C),我希望以不同的方式进行操作(例如,重新播放或终止循环)
目前,我的最佳选择是安装一个信号处理程序来监听
SIGINT
,但由于ZeroMQ
自己捕获信号,我更喜欢一种更复杂的方法。好的,有三种方法可以做到这一点,一种简单,两种混乱
清洁
最简单的方法是:
try {
zmq::poll(&items, number, timeout);
} catch (zmq::error_t &ex) {
if (ex.num() != EINTR) {
throw;
}
}
...
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[0].fd = 0; // Poll on stdin. 0 is the fd for stdin
pollOnThese[0].event = ZMQ_POLLIN;
pollOnThese[1].socket = &inSock; // This item polls inSock
pollOnThese[1].fd = 0; // This field is ignored because socket isn't NULL
pollOnThese[1].event = ZMQ_POLLIN;
while (!quitLoop)
{
zmq::poll(pollOnThese,2);
if (pollOnThese[0].revents == ZMQ_POLLIN)
{
// A key has been pressed, read it
char c;
read(0, &c, 1);
if (c == 'c')
{
quitloop = 1;
}
}
if (pollOnThese[1].revent == ZMQ_POLLIN)
{
// Handle inSock as required
}
}
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
pollOnThis.fd = 0; // This field is ignored because socket isn't NULL
pollOnThis.event = ZMQ_POLLIN;
while (!quitLoop)
{
try
{
zmq::poll(&pollOnThis, 1);
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
}
if (pollOnThis.revent == ZMQ_POLLIN && !quitLoop)
{
// Handle inSock as required
bool keepReading = true;
do
{
try
{
inSock.recv(&message)
keepReading = false;
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
else
{
// We also may want to test quitFlag here because the signal,
// being asynchronous, may have been delivered mid recv()
// and the handler would have set quitFlag.
if (quitFlag)
{
// Abort
keepReading = false;
}
else
{
// Some other signal interrupted things
// What to do? Poll has said a message can be read without blocking.
// But does EINTR mean that that meassage has been partially read?
// Has one of the myriad of system calls that underpin recv() aborted?
// Is the recv() restartable? Documentation doesn't say.
// Have a go anyway.
keepReading = true;
}
} // if
} // catch
}
while (keepReading);
<repeat this loop for every single zmq:: recv and zmq::send>
}
}
zmq::socket\u t inSock;
zmq::pollitem_t pollOnThese[2];
int-quitLoop=0;
<将inSock连接到某物的代码>
pollOnThese[0]。套接字=NULL;//此项未轮询ZMQ套接字
pollOnThese[0].fd=0;//按标准投票。0是标准输入的fd
pollOnThese[0]。事件=ZMQ_POLLIN;
pollOnThese[1]。套接字=&inSock;//这件物品在背包里
pollOnThese[1]。fd=0;//此字段被忽略,因为套接字不为NULL
pollOnThese[1]。事件=ZMQ_POLLIN;
而(!quitLoop)
{
zmq::poll(pollOnThese,2);
if(pollOnThese[0]。revents==ZMQ_POLLIN)
{
//按了一个键,读一下
字符c;
读取(0,&c,1);
如果(c=='c')
{
quitloop=1;
}
}
if(pollOnThese[1].revent==ZMQ_POLLIN)
{
//按要求进行内装处理
}
}
当然,这意味着您的“中止”不再是按CTRL-C的用户,他们只需按“C”键,或者任何可以写入此stdin的程序都可以发送“C”。或者,您也可以添加另一个ZMQ套接字作为命令通道。这里根本不允许信号,但我总是发现,将信号的使用与演员模型编程相结合是一件非常尴尬的事情。见下文
凌乱
使用信号的混乱方式如下所示:
try {
zmq::poll(&items, number, timeout);
} catch (zmq::error_t &ex) {
if (ex.num() != EINTR) {
throw;
}
}
...
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[0].fd = 0; // Poll on stdin. 0 is the fd for stdin
pollOnThese[0].event = ZMQ_POLLIN;
pollOnThese[1].socket = &inSock; // This item polls inSock
pollOnThese[1].fd = 0; // This field is ignored because socket isn't NULL
pollOnThese[1].event = ZMQ_POLLIN;
while (!quitLoop)
{
zmq::poll(pollOnThese,2);
if (pollOnThese[0].revents == ZMQ_POLLIN)
{
// A key has been pressed, read it
char c;
read(0, &c, 1);
if (c == 'c')
{
quitloop = 1;
}
}
if (pollOnThese[1].revent == ZMQ_POLLIN)
{
// Handle inSock as required
}
}
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
pollOnThis.fd = 0; // This field is ignored because socket isn't NULL
pollOnThis.event = ZMQ_POLLIN;
while (!quitLoop)
{
try
{
zmq::poll(&pollOnThis, 1);
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
}
if (pollOnThis.revent == ZMQ_POLLIN && !quitLoop)
{
// Handle inSock as required
bool keepReading = true;
do
{
try
{
inSock.recv(&message)
keepReading = false;
}
catch (zmq::error_t &ex)
{
if (ex.num() != EINTR)
{
throw;
}
else
{
// We also may want to test quitFlag here because the signal,
// being asynchronous, may have been delivered mid recv()
// and the handler would have set quitFlag.
if (quitFlag)
{
// Abort
keepReading = false;
}
else
{
// Some other signal interrupted things
// What to do? Poll has said a message can be read without blocking.
// But does EINTR mean that that meassage has been partially read?
// Has one of the myriad of system calls that underpin recv() aborted?
// Is the recv() restartable? Documentation doesn't say.
// Have a go anyway.
keepReading = true;
}
} // if
} // catch
}
while (keepReading);
<repeat this loop for every single zmq:: recv and zmq::send>
}
}
zmq::socket\u t inSock;
zmq::pollitem\u t pollOnThis;
int-quitLoop=0;
<将inSock连接到某物的代码>
pollOnThis.socket=&inSock;//这件物品在背包里
pollOnThis.fd=0;//此字段被忽略,因为套接字不为NULL
pollOnThis.event=ZMQ_POLLIN;
而(!quitLoop)
{
尝试
{
zmq::poll(&pollOnThis,1);
}
捕获(zmq::error\u t&ex)
{
如果(例如num()!=EINTR)
{
投掷;
}
}
if(pollOnThis.revent==ZMQ_POLLIN&&!quitLoop)
{
//按要求进行内装处理
bool-keepReading=true;
做
{
尝试
{
inSock.recv(&消息)
继续传播=错误;
}
捕获(zmq::error\u t&ex)
{
如果(例如num()!=EINTR)
{
投掷;
}
其他的
{
//我们可能还想在这里测试quitFlag,因为信号,
//由于是异步的,可能已在recv()中期交付
//处理程序会设置quitFlag。
如果(退出标志)
{
//流产
继续传播=错误;
}
其他的
{
//其他一些信号中断了事情
//怎么办?民意测验说,一条消息可以在不被阻止的情况下阅读。
//但EINTR是否意味着这条消息已经被部分阅读了?
//支持recv()的无数系统调用之一是否已中止?
//recv()是否可重启?文档没有说明。
//不管怎样,去试试吧。
持续=正确;
}
}//如果
}//抓住
}
同时(继续);
}
}
混合动力车(也很凌乱)
在ZeroMQ指南文档中,他们在某种程度上混合了这两种想法,让信号处理程序写入管道,并将管道包含在zmq_轮询中,就像我在上面使用stdin所做的那样
这是将异步信号转换为同步事件的常见技巧
但是,对于是否可以重新启动任何zmq例程,没有任何提示。他们只是利用它作为一种方式,在放弃任何正在进行的recv()或send()之后,启动一个干净的关机。如果为了完全中止,您需要完成一系列recv()和send()例程,那么如果正在使用信号,则无法保证这是可能的
如果我错了,请原谅我,但这感觉就像是你有意改变SIGINT的意思,而不是“立即终止整个程序”。如果是这样的话,你很可能想在以后恢复你的通信循环。在这种情况下,谁知道在信号到达后,recv()或send()调用是否可以恢复。zmq将使用大量的系统调用。他们中的许多人都是这样,而且不知道ZMQ是如何使用这些调用的(除了阅读他们的源代码)
结论
基本上,我只是避免完全使用信号,尤其是当您不想安装自己的处理程序时。通过使用stdin或管道或另一个ZMQ套接字(或实际上所有三个)作为ZMQ_poll()中包含的“中止”通道,您将提供一种简单而有效的中止循环的方法,并且使用它不会导致任何复杂情况如果我正确阅读了原始海报的问题,弗兰斯问是否有办法重新抛出C++中的某些例外。