Freigeben über


응답 없음(Not Responding)

가끔 Windows 운영체제를 사용하다보면 특정 애플리케이션이 아무런 반응을 보이지 않고 실행이 멈춘 듯한 모습을 볼 수 있습니다. 해당 어플리케이션의 타이틀바에는 '(응답 없음)'이라는 메시지가 추가되고 회색빛으로 해당 윈도우가 변해버리게 되는데, 작업 관리자에서 보면 해당 애플리케이션의 상태가 ' Not Responding(응답 없음)'이라고 표시됩니다. 이러한 증상은 해당 애플리케이션의 실행 중에 일시적으로 발생했다가 다시 정상 동작을 하기도 하고, 아니면 강제로 해당 프로세스를 종료하기 전까지는 계속 반응이 없는 상태가 되기도 합니다.

오늘은 이러한 'Not Responding' 상태에 대해서 살펴보도록 하겠습니다.

[그림] 작업관리자에서 프로세스 상태 확인하기

Windows의 작업 관리자는 어떻게 특정 애플리케이션이 응답 없는 상태인지 알고 'not resdponding'으로 표시해주는 걸까요? 이에 대한 답을 얻기 위해서는 윈도우 메카니즘에서 가장 기본이 되는 Windows Message부터 살펴봐야할 필요가 있습니다.

GUI 기반의 윈도우 운영체제로 오면서 기존의 CUI 환경과는 달리 사용자가 마우스를 이용해서 어떠한 애플리케이션에 어떠한 명령을 하려고 할지 미리 판단하기가 어려워졌습니다. 이를 위해서 애플리케이션은 '어떠한 이벤트가 발생하면 그것에 대해서 처리를 하는 식'으로 애플리케이션 개발 방법의 변화를 필요로 했는데 이를 'Event Driven Programming'이라고 합니다.

Win32 응용 프로그램을 이용해서 Hello World라는 샘플 프로그램을 개발해보신 분은 알겠지만, Window를 가지는 애플리케이션을 개발하기 위해서는 기본적으로 Window Object를 생성하고, 해당 Window에 대한 이벤트를 처리할 수 있는 Window Procedure를 제공해야 합니다. 그리고 각 Window는 자신의 Window에 대해 발생하고 처리되어야 하는 Window Message를 queue에 넣어두고 순서대로 이를 하나씩 빼와서 처리를 하게 됩니다. Window Message를 저장해두는 공간을 'Message Queue'라고 부르고, Message Queue에 메시지를 하나씩 가져와서 처리하는 과정을 반복하는 것을 'Message Loop'라고 합니다.

다음은 애플리케이션에서 일반적으로 가지고 있는 Message Loop믜 모습입니다. 애플리케이션은 처리해야할 메시지가 있으면 이에 대한 처리를 하는 과정을 반복함으로써 실행을 합니다. 만약 WM_QUIT 메시지를 전달받게 되면 Message Loop를 빠져나와서 return 하게됨으로써 애플리케이션은 종료되도록 구현되어 있습니다.

while (GetMessage(&Message,0,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
}
return;

위 메시지 루프에서 GetMessage()라는 Win32 API에 주의를 기울일 필요가 있습니다. GetMessage 함수 호출시 2,3,4번째 인자를 모두 0(NULL)로 전달했는데 이는 이 프로그램에서 생성한 모든 Window의 메시지를 받기 원한다는 것을 의미합니다. Windows 운영체제는 전달된 Message 구조체에 Message Queue에 있는 메시지를 Pop 시켜서 채우게 됩니다. GetMessage는 blocking function으로 만약 Message Queue에서 가져올 내용이 없으면 있을 때까지 대기했다가 return을 하는 함수 입니다.

Message Queue에서 메시지를 가져왔으면 Message 구조체 변수에 해당 메시지에 대한 내용이 채워져 있을 것입니다. 이것이 Virtual Key에 대한 처리인지 확인하기 위해서 먼저 TranslateMessage API를 호출하고, DispatchMessage API에 해당 Message 구조체를 다시 전달합니다. DispatchMessage를 호출하면 Windows 운영체제는 전달된 Message를 처리할 수 있도록 해당 Window Procedure를 호출합니다. 이는 Windows 운영체제가 해당 Window Procedure를 호출한다는 것을 의미합니다. Windows Procedure가 Callback 함수 형태를 가져야 하는 이유가 여기에 있습니다. Message의 Dispatching이 완료되면 다시 GetMessage 함수를 호출해서 다음 메시지를 기다리게 됩니다.

그런데 Windows 운영체제에서 특정 프로세스로 메시지를 전달했는데 해당 프로세스의 Message Loop에서 이 메시지를 가져가지 않으면 어떻게 될까요? 예를 들어 우편함에서 편지를 찾아가지 않아서 우편물이 쌓이게 되면 집배원이 해당 수신인이 부재중이거나 수신할 수 없는 상태라고 판단하게 되듯이, Windows 운영체제도 특정 시간동안 메시지를 받지 못하는 프로세스가 있으면 해당 프로세스를 '응답 없음(Not Responding)' 상태로 판단하게 됩니다. 어떠한 메시지도 받을 수 없기 때문에 키보드나 마우스 이벤트에 아무런 반응을 하지 않게 되고, 또한 x (종료버튼)을 클릭해도 프로세스가 종료되지 않습니다.

내부적으로 작업 관리자는 애플리케이션의 응답 상태를 확인하기 위해서 WM_GETICON 메시지를 애플리케이션의 main window로 SendMessage를 이용해서 전달합니다. 만약 해당 애플리케이션이 응답하지 않는다면 이를 "응답없음(Not Responding") 상태로 보게 됩니다.

참고로 다음은 MSDN에서 GetMessage에 대한 문서에 소개되어 있는 내용으로 Windows XP에서는 top level window가 not responding 상태가 되면 이를 대체하는 ghost window를 만들어 낸다는 내용이 있습니다.

Windows XP: If a top-level window stops responding to messages for more than several seconds, the system considers the window to be not responding and replaces it with a ghost window that has the same z-order, location, size, and visual attributes. This allows the user to move it, resize it, or even close the application. However, these are the only actions available because the application is actually not responding. When an application is being debugged, the system does not generate a ghost window. 

그러면 어떠한 경우에 프로세스가 응답없음 상태가 될까요. 여기에는 다음과 같은 몇가지 경우로 분류할 수 있습니다.

  • 프로그래밍 오류 : '무한루프'와 같은 프로그래밍 오류
  • 설계 오류 : 여러가지 경우가 있는데, waiting 하는 대상이 없는데 이를 무한 기다리도록 설계한 경우
  • 하드웨어 이슈 : 하드웨어 I/O 오류로 인해 이를 사용하는 프로그램이 응답없는 상태가 되는 경우
  • 악성 코드 : 바이러스나 스파이웨어로 인해 정상적인 실행이 불가능한 상태가 되는 경우

일시적으로 응답없음 상태였다가 다시 정상적으로 실행되는 것이 좋겠지만 강제 종료하지 않고는 이러한 증상이 없어지지 않는다면 처리 중인 데이터를 잃어버릴 수도 있지만 어쩔수 없이 작업 관리자의 'End Process' 명령을 내림으로써 해당 프로세스를 종료시켜야 됩니다. 이는 내부적으로 TerminateProcess Win32 API를 사용하고 있습니다.

Comments

  • Anonymous
    May 10, 2009
    The comment has been removed