ZeroMQ:如何对EINTR上的不同信号类型做出反应

时间:2016-03-16 15:45:38

标签: c++ signals zeromq

注意: This question非常接近我的,但我至少可以使用一些工作示例来提供解决方案,也许SELECT DISTINCT tew_cable.cab_tag, CASE WHEN L1_From.loc_loc_id = L2_From.loc_id THEN L1_From.loc_text END AS Rack_From, CASE WHEN L1_From.loc_loc_id = L2_From.loc_id THEN L2_From.loc_text ELSE L1_From.loc_text END AS Compartment_From, CASE WHEN C1_From.com_com_id = C2_From.com_id THEN C1_From.com_tag END AS PP_From, CASE WHEN C1_From.com_com_id = C2_From.com_id THEN SUBSTRING(com_tag, 1, CHARINDEX('\', com_tag)-1) END AS PP_From_INDEX, CASE WHEN C1_From.com_com_id = C2_From.com_id THEN LEFT(C1_From.com_tag,2) END AS PP_From_CHOP, CASE WHEN C1_From.com_com_id = C2_From.com_id THEN C2_From.com_tag ELSE C1_From.com_tag END AS Component_From FROM tew_cable LEFT JOIN tew_location AS L1_From LEFT JOIN tew_location AS L2_From ON L1_From.loc_loc_id = L2_From.loc_id ON tew_cable.cab_loc_id_from = L1_From.loc_id INNER JOIN tew_cablewire ON tew_cable.cab_id=tew_cablewire.caw_cab_id LEFT JOIN tew_wire ON tew_cablewire.caw_cab_id = tew_wire.wir_cab_id AND tew_cablewire.caw_no = tew_wire.wir_caw_no LEFT JOIN tew_component AS C1_From LEFT JOIN tew_component AS C2_From ON C1_From.com_com_id = C2_From.com_id ON C1_From.com_id = tew_wire.wir_com_idfrom 带来一些魔力我只是不知道。

目前我对阻止ZeroMQ调用的异常做出反应,如下所示:

ZeroMQ

我的意图是:重新抛出所有捕获的异常,但是由中断的系统调用触发的异常,我通常忽略(例如 {{1 }} ),然后重新启动try { zmq::poll(&items, number, timeout); } catch (zmq::error_t &ex) { if (ex.num() != EINTR) { throw; } } ...

如果 SIGPROF (CTRL-C)我想以不同的方式进行(例如,还要重新抛出或终止循环)。

目前我最好的办法是安装一个听 zmq::poll 的信号处理程序,但由于SIGINT自己捕获信号,我更喜欢更复杂的方法。

3 个答案:

答案 0 :(得分:1)

如果我正确地阅读了原始海报的问题,@ frans会询问是否有办法重新抛出某些异常,其中C ++异常包含EINTR错误代码,除了那些由某些信号。 @frans目前有一个SIGINT信号处理程序,并想知道是否有更清洁的方式。

提出了两个不同的问题,关于POSIX signal()处理及其与C ++异常的交互:

  • POSIX信号与C ++异常处理无关。
  • zmq::error_t是由zmq::poll()生成的C ++异常,是系统调用返回EINTR的结果。

TL; DR回答:不,没有更清洁的方式。

libzmq似乎没有安装自己的信号处理程序,但如果基础系统调用中断,它会抛出zmq::error_t EINTR(即poll()返回-1,errno被复制到zmq::error_t异常。)这可能意味着POSIX信号已经被传递并且特定于进程的处理程序运行,但还有其他原因。

POSIX / Unix / Linux / BSD signal()是一个操作系统工具,它指示内核中发生了异常。进程可以选择安装自己的处理程序以从情境中恢复,例如,SIGINTSIGQUIT处理程序关闭文件描述符,执行各种类型的清理等。这些处理程序位于 no方式与C ++异常处理有关。

其他警告:不要从POSIX / Unix / Linux / BSD信号处理程序中抛出C ++异常。这是先前讨论的in this SO topic

答案 1 :(得分:0)

好的,所以有三种方法可以做到这一点,一种是简单的,两种是凌乱的

<强>清洁

简单的方法就是这样:

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
   }
}

当然这意味着你的“中止”不再是用户按下CTRL-C,他们只需按下'c'键,或者任何可以写入此stdin的程序都可以发送'c'。或者,您也可以添加另一个ZMQ套接字作为命令通道。这里根本没有对信号的容忍度,但我总是发现将信号与Actor模型编程混合使用是一件非常尴尬的事情。见下文。

<强>凌乱

使用信号的混乱方式看起来像这样:

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>
   }
}

混合(也是凌乱)

在ZeroMQ指南文档here中,通过让信号处理程序写入管道并在zmq_poll中包含管道,就像我上面用stdin所做的那样,它们混合了这两个想法。

这是将异步信号转换为同步事件的常见技巧。

然而,关于是否可以重新启动任何zmq例程,根本没有任何提示。他们只是使用它作为一种方法来启动干净关闭,放弃了正在进行的任何recv()或send()。如果为了彻底中止你需要完成一系列recv()和send()例程,那么无法保证在使用信号时这是可能的。

如果我错了,请原谅我,但感觉你正在重新使用SIGINT,而不是“立即终止整个程序”。如果是这样,您可能希望稍后恢复通信循环。在这种情况下,谁知道在信号到达后是否可以恢复任何recv()或send()调用。 zmq将使用一大堆系统调用。其中许多都是not-restartable under certain circumstances,并且没有人知道ZMQ如何使用这些调用(缺少阅读源代码)。

<强>结论

基本上我只是避免完全使用信号,特别是因为你不想安装自己的处理程序。通过使用stdin或管道或其他ZMQ套接字(或实际上所有三个)作为zmq_poll()中包含的“中止”通道,您将提供一种简单有效的方法来中止您的循环,并且不会因此而导致并发症使用。

答案 2 :(得分:0)

我遇到了类似的问题,并且长期盯着这个问题及其答案,希望如果我足够努力地凝视,一个解决方案将会神奇地出现。

最后,我采用了方法detailed in this article

最重要的是,我们需要一个基于ppoll或更确切地说pselect的ZMQ轮询机制(我实现了一种,但是我仍然需要将其放在单独的库中)。这些p-变体将信号掩码作为附加参数。他们设置信号掩码,运行常规的poll / select,然后将信号掩码重置为其先前状态。这意味着在ppoll / pselect之外,您可以阻止所有期望的信号(阻止它们会导致它们在操作系统中排队,不会丢失),然后仅在{ {1}} / poll

这实际上是在您的轮询器中添加一个“信号套接字”。当轮询器从实际的套接字接收到信号时,轮询器将返回(带有select),或者当被允许通过使用信号掩码将其中断的信号中断时,则返回(带有0)。您可以使用信号处理程序来设置一些标志。

现在,在进行轮询调用之后,通常当您通常通过现在可以读取/写入的套接字时,首先要检查从信号处理程序设置的标志。信号在-1 / ppoll之外的任何地方都被阻止,因此可以肯定地知道,除了此处以外,您无需检查此标志。这样可以非常干净,紧凑且可靠地处理EINTR。

一些警告:要使其正常工作,您不能使用任何其他阻止调用,因此您的所有pselectsend都应处于非阻止状态(recv标志)。 / p>

另一件事是what you already mentioned:在使用库的情况下,用户应可以自由安装自己的信号处理程序。因此,如果他们想以健壮的方式使用您的库,可能需要指导用户如何以这种方式正确处理信号。我认为,如果将标志(或翻转它的函数)展示给用户,以便他们可以从自己的信号处理程序中调用它,那应该是可行的。