2012-11-26 3 views
4

나는 멀티 스레드 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 : 전혀 관련없는 문제로 인해 충돌이 발생했습니다. 그래도 지금은 모두 세계에서 좋습니다.

+1

이는 스레드 (뮤텍스) 함수가 신호 처리기 내에서 호출하기에 안전하지 않기 때문일 수 있습니다. –

+0

@ MarkB 나는 당신이 옳았다는 것을 확신합니다. 신호를 처리하는 일반적인 방법은 가끔씩 sigatomic_t를 가끔씩 폴링하는 것입니다. 신호가 설정되면 해당 변수를 설정하고 나가야합니다. "신호 안전 기능"이 있다는 것을 전혀 모르고 있었고 비 Windows 시스템에서도 실행 흐름에 어떤 영향이 미치는지에 대해 실제로 고려하지 않았습니다. 사실, 무슨 일이 벌어 졌는지 이해하는데 도움이되는 정말 좋은 기사입니다 : http://www.alexonlinux.com/signal-handling-in-linux 그것은 Linux 용으로 작성되었지만 적어도 주제를 다루고 있습니다. 정말로 필요합니다. – vmrob

+1

@ user1843978 : 시그널 핸들러로부터 안전한 것은 유일하게'volatile sigatomic_t' 타입의 변수를 수정하거나 C11에서도 원자 변수를 수정하거나 POSIX에서 async-signal-safe 함수를 호출하는 것입니다. – ninjalj

답변

2

디버거가 Ctrl-C 이벤트를 처리하고 있기 때문에 이것이 발생하는 것으로 생각됩니다. 콘솔 처리를 디버깅하고 CTRL + C 신호가 비활성화되지 않은 경우

시스템은 DBG_CONTROL_C 예외를 생성

MSDN article는 말 다음 갖는다. 이 예외는 디버거의 이점을 위해서만 발생하며 응용 프로그램은 예외 처리기를 사용하여 절대로 처리해서는 안됩니다. 디버거가 예외를 처리하는 경우 응용 프로그램에서 경고 대기가 종료되는 경우를 제외하고 Ctrl + C를 알 수 없습니다. 디버거가 처리되지 않은 예외를 전달하면 앞에서 설명한 것처럼 CTRL + C가 콘솔 프로세스로 전달되고 신호로 처리됩니다.

"출력 - 처리되지 않음"으로 이벤트 필터를 설정하여 응용 프로그램에서 처리하도록 허용 할 수 있습니다. WinDbg에서이를 설정하는 방법에 대한 스크린 샷을 첨부했습니다. Visual Studio에서는이 항목을 "Win32 예외"에 나열합니다.

WinDBG Event Filters

편집 : 또한 , 내가 이벤트 핸들러에서 뮤텍스를 잠그려고하는 나쁜 관행으로 간주되는 추가해야합니다. 시그널 핸들러가 호출 될 때 뮤텍스가 이미 획득 된 경우 시그널 핸들러가 완료되고 시그널 핸들러가 뮤텍스가 획득 될 때까지 완료 할 수 없을 때까지 애플리케이션이 다시 시작할 수 없으므로 데드락이 발생할 것이다. 이 경우에는 사용하지 않을 수도 있지만 spurious wake-up 또는 CTRL-C를 연속해서 사용하는 중 Ctrl-C를 사용하면 교착 상태가 발생할 수 있습니다.

+0

나는 실제로이 신호를 잡아 내지 못하게 막아 버렸기 때문에 잠시 전에이 코드를 테스트하여 문제가 생기기 시작했다. – vmrob