c – 信号处理期间的堆损坏

我有一个多线程
Windows服务器,我正在研究,发现在我通过control-c关闭程序时出现一定的标准后,它崩溃了.如果我的服务器从客户端接收数据包然后我使用control-c,它会崩溃.如果我启动我的服务器,让它等待任何时间段的数据包,然后使用control-c,它会正常退出.

然而,有什么奇怪的是,即使程序确实抛出异常(除非那是正常的),我的所有线程都报告他们正以状态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
}

互斥和条件变量都是全局变量:

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

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

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

当然,当我注释掉有问题的线路时,程序根本不响应信号.我可以有一个wait_for,这样我就不必通知信号处理循环它有一个新信号,但我不认为这是最好的方法.

我确实从MSDN上读到了有关信号的内容:

When a CTRL+C interrupt occurs, Win32 operating systems generate a new
thread to specifically handle that interrupt.

Because signal-handler routines are usually called asynchronously when an interrupt
occurs, your signal-handler function may get control when a run-time operation is
incomplete and in an unknown state.

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

那么,接近信号的最佳方法是什么?我遇到什么问题?

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

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

看起来所有其他线程都消失了.我想我可能在其他地方犯了一个错误.

感谢您清除信号功能的安全性.

编辑2:看起来像一个不相关的共享指针给我带来麻烦!我很高兴能看到一些好消息.

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

最佳答案 我怀疑这是因为您的调试器正在处理Ctrl-C事件.

这个MSDN article有如下说法:

If a console process is being debugged and CTRL+C signals have not been disabled, the system generates a DBG_CONTROL_C exception. This exception is raised only for the benefit of the debugger, and an application should never use an exception handler to deal with it. If the debugger handles the exception, an application will not notice the CTRL+C, with one exception: alertable waits will terminate. If the debugger passes the exception on unhandled, CTRL+C is passed to the console process and treated as a signal, as previously discussed.

您可以将事件过滤器设置为“输出 – 未处理”以允许您的应用程序处理此事件.我附上了一个如何在WinDbg中设置它的屏幕截图. Visual Studio在“Win32 Exceptions”下列出了这一点.

编辑:
此外,我应该补充说,尝试在事件处理程序中锁定互斥锁被认为是不好的做法.如果在调用信号处理程序时已经获取了互斥锁,则会导致死锁,因为应用程序无法恢复,直到信号处理程序完成并且信号处理程序无法完成,直到获取互斥锁为止.虽然在您的使用案例中这不太可能,但在spurious wake-up或2 CTRL-C的背靠背中的CTRL-C可能会导致死锁.

点赞