ZeroMQ socket.recv()引发了STACK_OVERFLOW异常

时间:2017-11-30 16:57:22

标签: c++ windows zeromq

如果在.dll中使用此代码,则对 socket.recv() 的调用会引发异常STACK_OVERFLOW,但此代码已编译.exe

为什么吗

我运行.dll - 测试" C:\windows\system32\rundll32.exe myDll.dll StartUp"

void StartUp()
{
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);

socket.bind("tcp://127.0.0.1:3456");
zmq::message_t msgIN, msgOUT("test", 4);
while (true){


    socket.recv(&msgIN);

    socket.send(msgOUT);
};
}

callstack:

libzmq-v120-mt-gd-4_2_2.dll!zmq::mailbox_t::recv(zmq::command_t * cmd_=0x0231f700, int timeout_=0x00000000) 

libzmq-v120-mt-gd-4_2_2.dll!zmq::io_thread_t::in_event() 

libzmq-v120-mt-gd-4_2_2.dll!zmq::select_t::loop() 

libzmq-v120-mt-gd-4_2_2.dll!zmq::select_t::worker_routine(void * arg_=0x002f1778) 
libzmq-v120-mt-gd-4_2_2.dll!thread_routine(void * arg_=0x002f17c0) 

主线程callstack:

libzmq-v120-mt-gd-4_2_2.dll!zmq::signaler_t::wait(int timeout_=0xffffffff)
libzmq-v120-mt-gd-4_2_2.dll!zmq::mailbox_t::recv(zmq::command_t * cmd_=0x0019f3c0, int timeout_=0xffffffff) 
libzmq-v120-mt-gd-4_2_2.dll!zmq::socket_base_t::process_commands(int timeout_, bool throttle_)
libzmq-v120-mt-gd-4_2_2.dll!zmq::socket_base_t::recv(zmq::msg_t * msg_=0x0019f628, int flags_=0x00000000)
libzmq-v120-mt-gd-4_2_2.dll!s_recvmsg(zmq::socket_base_t * s_=0x006f6c70, zmq_msg_t * msg_=0x0019f628, int flags_=0x00000000) 
libzmq-v120-mt-gd-4_2_2.dll!zmq_msg_recv(zmq_msg_t * msg_=0x0019f628, void * s_=0x006f6c70, int flags_=0x00000000)
mydll.dll!zmq::socket_t::recv(zmq::message_t * msg_=0x0019f628, int flags_=0x00000000)
mydll.dll!StartUp() 

更新

这个例子,同样的原因也崩溃了。有人知道异常堆栈溢出的原因吗?

zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);

socket.bind("tcp://*:7712");

while (1){
    Sleep(10);
}

反向问题隔离MCVE:

这个 myDll.dll - 测试是如何工作的, 如果由C:\windows\system32\rundll32.exe myDll.dll StartUp运行?发布屏幕输出。

void StartUp()
{
     std::cout << "INF:: ENTRY POINT ( C:\windows\system32\rundll32.exe myDll.dll StartUp )" << std::endl;
     std::cout << "INF:: WILL SLEEP  ( C:\windows\system32\rundll32.exe myDll.dll StartUp )" << std::endl;
     Sleep( 10 );
     std::cout << "INF:: SLEPT WELL  ( C:\windows\system32\rundll32.exe myDll.dll StartUp )" << std::endl;
     std::cout << "INF:: WILL RETURN ( C:\windows\system32\rundll32.exe myDll.dll StartUp )" << std::endl;
}

2 个答案:

答案 0 :(得分:0)

崩溃的原因是OPTIONAL_HEADER rundll32文件中的SizeOfStackCommit值。 它太小(0xC000),我把它改为0x100000。现在一切正常。

答案 1 :(得分:-1)

ZeroMQ对象需要某些方面才能使用:

雷达下有很多功能,可能会破坏您的屏幕上的残骸。

最佳读取ZeroMQ C ++绑定参考文档和原始ZeroMQ API(通常在C ++绑定中也提到)。

两者都强调永远不要直接处理zmq::message_t实例,而是通过使用&#34; service&#34; -functions(通常在C ++中重新包装为实例方法)。

zmq::message_t messageIN,
               messageOUT;

bool successFlag;

while (true){

             successFlag  = socket.recv( &messageIN );
     assert( successFlag && "EXC: .recv( &messageIN )" );
                        /* The zmq_recv() function shall receive a message
                           from the socket referenced by the socket argument
                           and store it in the message referenced by the msg
                           argument.
                           Any content previously stored in msg shall be
                           properly deallocated.
                           If there are no messages available on the specified
                           socket the zmq_recv() function shall block
                           until the request can be satisfied.
                           */

                       messageOUT.copy(  messageIN );

             successFlag  = socket.send(  messageOUT );
     assert( successFlag && "EXC: .send(  messageOUT )" );
                        /* The zmq_send() function shall queue the message
                           referenced by the msg argument to be sent to
                           the socket referenced by the socket argument.
                           The flags argument is a combination of the flags
                           defined { ZMQ_NOBLOCK, ZMQ_SNDMORE }

                           The zmq_msg_t structure passed to zmq_send()
                           is nullified during the call.

                           If you want to send the same message to multiple
                           sockets you have to copy it using (e.g.
                           using zmq_msg_copy() ).

                           A successful invocation of zmq_send()
                           does not indicate that the message
                           has been transmitted to the network,
                           only that it has been queued on the socket
                           and ØMQ has assumed responsibility for the message.
                           */
};

我怀疑是一个引用计数,添加越来越多的实例,由zmq::message_t message;构造函数在无限while( true ){...} - 循环中生成,其中没有一个遇到它自己公平的析构函数。具有物理限制容量且在DLL内部没有STACK管理关注的STACK迟早会失败。

zmq::message_t 实例是一个相当昂贵的玩具,因此对于专业代码来说,总是欢迎良好的资源管理实践(预分配,重用,受控破坏)。

Q.E.D。

为了清晰起见,尾部备注:

有点解释Dijkstra关于错误搜索和软件测试的观点:&#34;如果我没有看到错误,那并不意味着,代码片段中没有错误(如果有任何外部函数则更少)除此之外还链接。)&#34;

没有堆叠分配?

是的,没有可见的

ZeroMQ API为它带来了更多亮点:

  

&#34; zmq_msg_init_size()函数应分配存储消息大小字节所需的任何资源,并初始化msg引用的消息对象以表示新分配的消息。

     

实现应选择将消息内容存储在堆栈(小消息)还是堆栈(大消息)上。出于性能原因,zmq_msg_init_size()不得清除消息数据。&#34;

多年以来,迄今为止花在使用基于ZeroMQ API的跨平台分布式系统上的v.2.1 +,已经教会了我很多关注显式资源控制。一旦你没有为本机API开发自己的语言绑定就越多。

在所有不受支持的批评之后,让我们再添加一份来自ZeroMQ的引文:

这增加了一个视图,如何通过库C ++绑定本身对message_t内容进行适当的间接操作,包含在琐碎的辅助函数中:

  

来自 zhelpers.hpp

//  Receive 0MQ string from socket and convert into string
static std::string
s_recv (zmq::socket_t & socket) {

    zmq::message_t message;
    socket.recv(&message);

    return std::string(static_cast<char*>(message.data()), message.size());
}

//  Convert string to 0MQ string and send to socket
static bool
s_send (zmq::socket_t & socket, const std::string & string) {

    zmq::message_t message(string.size());
    memcpy (message.data(), string.data(), string.size());

    bool rc = socket.send (message);
    return (rc);
}

//  Sends string as 0MQ string, as multipart non-terminal
static bool
s_sendmore (zmq::socket_t & socket, const std::string & string) {

    zmq::message_t message(string.size());
    memcpy (message.data(), string.data(), string.size());

    bool rc = socket.send (message, ZMQ_SNDMORE);
    return (rc);
}