나는 멀티 스레드 Windows 서버를 사용하고 있는데, control-c를 통해 프로그램을 닫는 동안 특정 조건 집합이 발생하면 충돌이 발생합니다. 내 서버가 클라이언트로부터 패킷을받은 다음 control-c를 사용하면 충돌이 발생합니다. 서버를 가동 시키면 일정 시간 동안 패킷을 기다린 다음 control-c를 사용하면 제대로 종료됩니다.신호 처리 중 힙 손상
내 스레드가 모두 정상 상태가 아니라면 프로그램이 예외를 throw하더라도 상태 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에서 무언가를 읽었다하십시오 CTRL + C 인터럽트가 발생하면
는 Win32에서 운영 체제는 특히 그 인터럽트를 처리 할 수있는 새로운 스레드를 생성합니다.인터럽트가 발생하면 signal-handler 루틴은 비동기 적으로 호출되기 때문에 런타임 작업이 불완전하고 알 수없는 상태 일 때 signal-handler 함수가 제어를받을 수 있습니다.
나는이 경우에 적용되는지 솔직하게 확신하지 않습니다. 그렇다면 신호 처리기를 호출 할 때 내 뮤텍스가 존재할 수도 있고 존재하지 않을 수도 있다는 것을 의미합니까?
그런 다음 신호에 접근하는 가장 좋은 방법은 무엇입니까? 내가 겪고있는 문제는 무엇입니까?
편집 : 그냥 몇 가지를 정리하기 : 프로그램이 더 조금 진행처럼
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 : 전혀 관련없는 문제로 인해 충돌이 발생했습니다. 그래도 지금은 모두 세계에서 좋습니다.
이는 스레드 (뮤텍스) 함수가 신호 처리기 내에서 호출하기에 안전하지 않기 때문일 수 있습니다. –
@ MarkB 나는 당신이 옳았다는 것을 확신합니다. 신호를 처리하는 일반적인 방법은 가끔씩 sigatomic_t를 가끔씩 폴링하는 것입니다. 신호가 설정되면 해당 변수를 설정하고 나가야합니다. "신호 안전 기능"이 있다는 것을 전혀 모르고 있었고 비 Windows 시스템에서도 실행 흐름에 어떤 영향이 미치는지에 대해 실제로 고려하지 않았습니다. 사실, 무슨 일이 벌어 졌는지 이해하는데 도움이되는 정말 좋은 기사입니다 : http://www.alexonlinux.com/signal-handling-in-linux 그것은 Linux 용으로 작성되었지만 적어도 주제를 다루고 있습니다. 정말로 필요합니다. – vmrob
@ user1843978 : 시그널 핸들러로부터 안전한 것은 유일하게'volatile sigatomic_t' 타입의 변수를 수정하거나 C11에서도 원자 변수를 수정하거나 POSIX에서 async-signal-safe 함수를 호출하는 것입니다. – ninjalj