信号处理期间堆损坏

我有一个multithreading的Windows服务器,我正在工作,并发现,当我通过控制cclosures程序时,发生了一定的标准,它崩溃。 如果我的服务器从客户端收到数据包,然后使用control-c,则会崩溃。 如果我启动服务器,让它等待数据包一段时间,然后使用control-c,它会正常退出。

这有什么奇怪的是,我的所有线程都报告说,即使程序抛出一个exception(除非这是正常的),它们将以状态0退出。

First-chance exception at 0x75A16DA7 (kernel32.dll) in server.exe: 0x40010005: Control-C. HEAP[server.exe]: HEAP: Free Heap block 96a818 modified at 96a908 after it was freed server.exe has triggered a breakpoint. The thread 0xc34 has exited with code 0 (0x0). The thread 0x1c64 has exited with code 0 (0x0). The thread 0xdbc has exited with code 0 (0x0). The thread 0x117c has exited with code 0 (0x0). The thread 0x1444 has exited with code 0 (0x0). The thread 0x1d60 has exited with code 0 (0x0). The thread 0x798 has exited with code 0 (0x0). The thread 0x700 has exited with code 0 (0x0). The thread 0x1bbc has exited with code 0 (0x0). The thread 0x1b74 has exited with code 0 (0x0). The program '[7528] server.exe' has exited with code 0 (0x0). 

这部分代码似乎是这个问题的原因:

 void handleSignal(int sig) { std::unique_lock<std::mutex> lock(signalMutex); // <-- comment out and it doesn't crash signaled = true; _receivedSignal = sig; signalHandlerCondition.notify_one(); // <-- comment out and it doesn't crash } 

互斥和条件variables都是全局variables:

 std::mutex signalMutex; std::condition_variable signalHandlerCondition; 

我有一个专用的信号处理线程,试图在该事件通知时正常closures服务器。

 void run() { while (gContinueRunning && _continueRunning) { std::unique_lock<std::mutex> lock(signalMutex); signalHandlerCondition.wait(lock); if (signaled) { gContinueRunning = false; signaled = false; Server::stop(); } } } 

当然,当我评论这些违规线路时,该程序根本不响应信号。 我可以等待一段时间,这样我就不必通知信号处理环路它有一个新的信号,但是我不认为这是最好的方法。

我从MSDN读了一些关于信号的东西:

当发生CTRL + C中断时,Win32操作系统会生成一个新线程来专门处理该中断。

由于信号处理程序例程通常在发生中断时被asynchronous调用,因此当运行时操作不完整且处于未知状态时,您的信号处理函数可能会得到控制权。

我真的不确定这是否适用于这种情况。 如果是这样,这是否意味着当调用信号处理程序时,我的互斥锁可能存在也可能不存在?

那么,处理信号的最好方法是什么? 我在这里遇到什么问题?


编辑:只是为了清除一些事情:

 void start() { _receivedSignal = 0; _continueRunning = true; // start thread std::thread signalHandlerThread(run); _signalHandlerThread = std::move(signalHandlerThread); // register signals signal(SIGABRT, SignalHandler::handleSignal); signal(SIGTERM, SignalHandler::handleSignal); signal(SIGINT, SignalHandler::handleSignal); } 

即使在删除互斥锁后,看起来程序的进展情况也有所改进 – 不过直到主要完成。

 msvcr110d.dll!operator delete(void * pUserData) Line 52 C++ server.exe!std::_Ref_count<User>::_Destroy() Line 161 C++ server.exe!std::_Ref_count_base::_Decref() Line 120 C++ server.exe!std::_Ptr_base<User>::_Decref() Line 347 C++ server.exe!std::shared_ptr<User>::~shared_ptr<User>() Line 624 C++ server.exe!std::pair<unsigned int const ,std::shared_ptr<User> >::~pair<unsigned int const ,std::shared_ptr<User> >() C++ server.exe!std::pair<unsigned int const ,std::shared_ptr<User> >::`scalar deleting destructor'(unsigned int) C++ server.exe!std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 624 C++ server.exe!std::allocator_traits<std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > & _Al, std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 758 C++ server.exe!std::_Wrap_alloc<std::allocator<std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> > >::destroy<std::pair<unsigned int const ,std::shared_ptr<User> > >(std::pair<unsigned int const ,std::shared_ptr<User> > * _Ptr) Line 909 C++ server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::_Erase(std::_Tree_node<std::pair<unsigned int const ,std::shared_ptr<User> >,void *> * _Rootnode) Line 2069 C++ server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::clear() Line 1538 C++ server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::erase(std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<unsigned int const ,std::shared_ptr<User> > > > > _First, std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<unsigned int const ,std::shared_ptr<User> > > > > _Last) Line 1512 C++ server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::_Tidy() Line 2216 C++ server.exe!std::_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >::~_Tree<std::_Tmap_traits<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > >,0> >() Line 1190 C++ server.exe!std::map<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > > >::~map<unsigned int,std::shared_ptr<User>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::shared_ptr<User> > > >() C++ server.exe!`dynamic atexit destructor for 'User::_usersListBySession''() C++ msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 584 C msvcr110d.dll!exit(int code) Line 394 C server.exe!__tmainCRTStartup() Line 549 C server.exe!mainCRTStartup() Line 377 C 

它看起来像所有其他线程都没有了。 我想我可能在别处犯了一个错误。

谢谢你清理信号function的安全。


编辑2:看起来像一个不相关的共享指针正在造成我的麻烦! 我很高兴看到一些很好的东西。

编辑3:一个完全不相关的问题导致崩溃。 现在世间一切都好。

我怀疑这是因为你的调试器正在处理Ctrl-C事件。

这个MSDN文章有以下说:

如果正在调试控制台进程并且CTRL + C信号未被禁用,则系统会生成DBG_CONTROL_C异常。 这个异常只是为了调试器的好处而提出的,应用程序不应该使用异常处理器来处理它。 如果调试器处理异常,应用程序将不会注意到CTRL + C,但有一个例外:可警告的等待将终止。 如果调试器在未处理的情况下传递异常,则CTRL + C将传递给控制台进程,并作为信号处理,如前所述。

你可以设置你的事件过滤器“输出 – 不处理”,让你的应用程序来处理这个。 我附上了一个如何在WinDbg中设置的屏幕显示。 Visual Studio在“Win32 Exceptions”下列出。

WinDBG事件筛选器

编辑:另外,我应该补充说,试图锁定事件处理程序中的互斥锁被认为是不好的做法。 如果信号处理程序被调用时已经获得了互斥量,则会导致死锁,因为应用程序无法恢复,直到信号处理程序完成并且信号处理程序无法完成,直到获取互斥量为止。 虽然这在您的用例中不太可能发生,但虚假唤醒或CTRL-C背靠背的CTRL-C可能导致死锁。