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()中包含的“中止”通道,您将提供一种简单而有效的中止循环的方法,并且使用它不会导致任何复杂情况