맨위로가기

이벤트 루프

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

이벤트 루프는 그래픽 사용자 인터페이스를 갖춘 현대 애플리케이션의 핵심 기능으로, 메시지를 수신하고 처리하는 메인 루프를 의미한다. 운영체제에서 제공하는 `get_next_message()` 루틴을 사용하여 메시지가 있을 때만 루프가 실행되며, 파일 인터페이스, 다양한 운영체제 환경에서의 구현, 다른 설계 방식과의 비교 등을 통해 이벤트 루프의 동작 방식을 설명한다.

더 읽어볼만한 페이지

  • 이벤트 (컴퓨팅) - 메시지 큐
    메시지 큐는 프로세스나 스레드 간 비동기 통신을 제공하여 송신자와 수신자가 동시에 상호 작용할 필요 없이 메시지를 교환하도록 하며, 메시지는 수신자가 검색할 때까지 저장되고 시스템 장애 시 복원력을 제공하지만 보안 취약점, 특정 기업 종속성 등의 논란도 존재한다.
  • 이벤트 (컴퓨팅) - 비동기 입출력
    비동기 입출력은 입출력 완료를 기다리지 않고 다른 작업을 처리하는 방식으로, 완료 시 콜백이나 시그널을 통해 결과를 알려주어 효율적인 자원 활용과 성능 향상을 가져다준다.
  • 제어 흐름 - 프로그램 카운터
    프로그램 카운터는 CPU 내에서 다음에 실행될 명령어의 주소를 저장하는 레지스터로, 명령어 사이클의 fetch 단계에서 사용되어 명령어를 가져오고 실행 후 갱신되며, CPU 성능 향상 기술과 현대 프로그래밍 모델에 영향을 미친다.
  • 제어 흐름 - 예외 처리
    예외 처리는 프로그램 실행 중 예외 발생 시 정상적인 실행 흐름을 유지하거나 안전하게 종료하기 위한 메커니즘으로, 많은 프로그래밍 언어에서 제공하며 예외 안전성을 목표로 한다.
이벤트 루프

2. 메시지 전달

현대의 대부분의 응용 프로그램들은 그래픽 사용자 인터페이스가 널리 사용되면서 메인 루프 기능을 갖추고 있다. `get_next_message()` 루틴은 보통 운영 체제에서 제공되며, 메시지를 사용할 수 있을 때까지 차단된다. 따라서 루프는 처리할 대상이 있을 때만 진입한다.

메시지 펌프는 프로그램의 메시지 큐(운영 체제에서 할당하고 소유하는 것이 일반적)에서 처리할 메시지를 프로그램으로 '펌프'하는 역할을 한다. 엄밀히 말하면, 이벤트 루프는 프로세스 간 통신을 구현하는 방법 중 하나이다. 실제로 메시지 처리는 Mach 운영 체제의 커널 레벨 구성 요소를 포함한 많은 시스템에서 존재한다. 이벤트 루프는 메시지 전달을 사용하는 시스템의 구체적인 구현 기술이다.

메시지 펌프라는 이름은 프로그램의 메시지 큐(일반적으로 OS가 할당하고 소유)에서 메시지를 가져와 해당 프로그램 내에서 처리하는 데서 유래한다. 엄밀히 말하면, 이벤트 루프는 프로세스 간 통신 구현 방법 중 하나이다. 사실 메시지 처리는 많은 시스템에 존재하며, 예를 들어 Mach의 커널 레벨 구성 요소에도 있다. 이벤트 루프는 메시지를 사용하는 시스템의 구현 기술 중 하나이다.

3. 파일 인터페이스

유닉스에서는 "모든 것은 파일이다"라는 패러다임에 따라 파일 기반 이벤트 루프가 자연스럽게 만들어졌다. 파일 읽기/쓰기, 프로세스 간 통신, 네트워크 통신, 장치 제어는 모두 파일 입출력을 통해 이루어지며, 파일 디스크립터를 통해 대상을 식별한다. select와 poll 시스템 호출을 사용하면 파일 디스크립터 집합의 상태 변화(예: 데이터가 읽을 수 있게 되는 경우)를 감지할 수 있다.

예를 들어, 지속적으로 업데이트되는 파일을 읽고 소켓(유닉스 도메인 또는 버클리)을 통해 클라이언트와 통신하며 X 윈도우 시스템에 내용을 표시하는 프로그램을 생각해 볼 수 있다.

3. 1. 시그널 처리

유닉스에서 파일 인터페이스를 따르지 않는 몇 안 되는 예로 비동기 이벤트인 시그널이 있다. 시그널은 시그널 핸들러에서 수신하는데, 이는 작업의 나머지 부분이 일시 중단되는 동안 실행되는 작고 제한된 코드 조각이다. 시그널이 수신되어 처리되는 동안 작업이 `select()`에서 차단되면 select는 EINTR로 조기에 반환된다. 신호가 작업이 CPU를 사용하는 동안 수신되면 신호 처리기가 반환될 때까지 작업은 명령어 사이에 일시 중단된다.

따라서 신호를 처리하는 명백한 방법은 신호 처리기가 전역 플래그를 설정하고 이벤트 루프가 `select()` 호출 전후에 즉시 해당 플래그를 확인하도록 하는 것이다. 플래그가 설정된 경우 파일 디스크립터의 이벤트와 동일한 방식으로 신호를 처리한다. 불행히도, 이렇게 하면 경쟁 조건이 발생한다. 플래그를 확인하고 `select()`를 호출하는 사이에 신호가 즉시 도착하면, 다른 이유(예: 좌절한 사용자에 의해 중단되는 경우)로 `select()`가 반환될 때까지 처리되지 않는다.

POSIX에서 제시한 해결책은 `select()`와 유사하지만 추가적인 `sigmask` 매개변수를 사용하는 `pselect()` 호출이다. 이 매개변수는 ''신호 마스크''를 설명한다. 이를 통해 응용 프로그램은 주 작업에서 신호를 마스크한 다음 `select()` 호출 기간 동안 마스크를 제거하여 응용 프로그램이 I/O 바운드 상태인 동안에만 신호 처리기가 호출되도록 할 수 있다. 그러나 `pselect()`의 구현은 항상 신뢰할 수 있는 것은 아니다. 2.6.16 이전 버전의 리눅스에는 `pselect()` 시스템 호출이 없었으며,[1] glibc가 `pselect()`가 피하고자 하는 바로 그 경쟁 조건에 취약한 방식을 통해 이를 에뮬레이션하도록 강요했다.

더욱 이식 가능한 대안은 비동기 이벤트를 ''셀프 파이프 트릭''을 사용하여 파일 기반 이벤트로 변환하는 것이다.[2] 이는 "신호 처리기가 파이프에 바이트를 쓰고, 그 다른 끝은 메인 프로그램에서 `select()`로 모니터링됩니다."[3] 리눅스 커널 버전 2.6.22에서는 새로운 시스템 호출 `signalfd()`가 추가되어 특수 파일 디스크립터를 통해 신호를 수신할 수 있다.

4. 다양한 환경에서의 구현

이벤트 루프는 다양한 환경에서 구현되며, 운영 체제 및 사용되는 프로그래밍 프레임워크에 따라 그 방식이 달라진다.

이벤트 루프는 프로세스 간 통신을 구현하는 한 가지 방법이며, 메시지 전달을 사용하는 시스템에서 구체적인 구현 기술로 활용된다. 메시지 펌프는 프로그램의 메시지 큐에서 메시지를 가져와 처리하는 역할을 수행한다.

웹 페이지와 자바스크립트는 보통 단일 스레드 웹 브라우저 프로세스에서 실행된다. 이때 브라우저 프로세스는 큐에 있는 메시지를 한 번에 하나씩 처리한다.

GUI 환경에서 많은 애플리케이션은 메인 루프를 가진다. `get_next_message()` 루틴은 대개 운영체제(OS)에서 제공하며, 메시지가 도착할 때까지 블록된다.

```

'''function''' main

initialize()

'''while''' message != quit

message := get_next_message()

process_message(message)

'''end''' '''while'''

'''end''' '''function'''

4. 1. 윈도우 애플리케이션

마이크로소프트 윈도우 운영 체제에서 사용자와 상호 작용하는 프로세스는 들어오는 메시지를 수락하고 이에 반응해야 하며, 이는 해당 프로세스의 메시지 루프에 의해 수행된다. 윈도우에서 메시지는 운영 체제에 의해 생성되고 부과되는 이벤트와 동일하다.[7] 이벤트는 사용자 상호 작용, 네트워크 트래픽, 시스템 처리, 타이머 활동, 프로세스 간 통신 등이 될 수 있다. 비대화형, I/O 전용 이벤트의 경우 윈도우에는 입출력 완료 포트가 있으며, I/O 완료 포트 루프는 메시지 루프와 별도로 실행되며 메시지 루프와 상호 작용하지 않는다.

대부분의 Win32 응용 소프트웨어의 "핵심"은 [https://msdn.microsoft.com/en-us/library/ms633559.aspx WinMain()] 함수이며, 이 함수는 루프에서 [https://msdn.microsoft.com/en-us/library/ms644936.aspx GetMessage()]를 호출한다. GetMessage()는 메시지 또는 "이벤트"가 수신될 때까지 차단된다(비차단 대안으로 [https://msdn.microsoft.com/en-us/library/ms644943.aspx PeekMessage()] 함수 사용).[11] 선택적 처리가 수행된 후, [https://msdn.microsoft.com/en-us/library/ms644934.aspx DispatchMessage()]를 호출하여 관련 핸들러, 즉 WindowProc에 메시지를 보낸다. 일반적으로 특별한 WindowProc()이 없는 메시지는 기본 WindowProc인 DefWindowProc로 전송된다. DispatchMessage()는 메시지의 HWND 핸들(함수 [https://msdn.microsoft.com/en-us/library/ms633586.aspx RegisterClass()]로 등록됨)의 WindowProc을 호출한다.[8][9][10]

MFC, 윈도우 폼, WPF와 같은 애플리케이션 프레임워크에서는 라이브러리 측에서 기본 메시지 루프를 구현하고 있기 때문에, 일반적으로 애플리케이션 코드에서 명시적으로 기술할 필요는 없다. 애플리케이션에서 필요로 하는 이벤트 핸들러만 기술해 가는 「이벤트 구동 프로그래밍」 스타일을 사용하여 효율적으로 개발할 수 있다. 단, 필요에 따라 메시지 루프를 상세하게 커스터마이즈하거나,[13] 다른 프레임워크를 상호 운용하기 위한 API도 준비되어 있다.[14][15]

4. 1. 1. 메시지 순서

현대의 대부분의 응용 프로그램들은 그래픽 사용자 인터페이스가 널리 사용되면서 메인 루프 기능을 갖추고 있다. `get_next_message()` 루틴은 보통 운영 체제에서 제공되며, 메시지를 사용할 수 있을 때까지 차단한다. 따라서 루프는 처리할 대상이 있을 때에만 진입한다.

```

'''function''' main

initialize()

'''while''' message != quit

message := get_next_message()

process_message(message)

'''end''' '''while'''

'''end''' '''function'''

```

최근(2019년 8월) 마이크로소프트 윈도우는 프로그래머에게 메시지가 시스템 및 주변 장치에 의해 감지된 순서대로 애플리케이션의 메시지 루프에 전달되도록 보장한다. 이러한 보장은 다중 스레드 애플리케이션 설계 결과를 고려할 때 필수적이다.[4]

다만, 항상 마지막으로 수신되는 메시지나 문서화된 다른 우선순위를 가진 메시지처럼 일부 메시지에는 다른 규칙이 적용된다.[4][12]

4. 2. X 윈도우 시스템

유닉스에서 모든 것이 파일이다라는 개념은 파일 기반 이벤트 루프가 자연스럽게 만들어지게 된 배경이 된다. 파일 읽기/쓰기, 프로세스 간 통신, 네트워크 통신, 장치 제어는 모두 파일 입출력을 통해 이루어지며, 대상은 파일 서술자로 식별된다. `select`와 `poll` 시스템 호출은 파일 서술자 집합의 상태 변화(예: 데이터가 읽을 수 있게 되는 경우)를 감지한다.

예를 들어, 지속적으로 업데이트되는 파일을 읽고 소켓(유닉스 도메인 또는 버클리)을 통해 클라이언트와 통신하며, X 윈도 시스템에 내용을 표시하는 프로그램을 생각해 볼 수 있다.

X 응용 프로그램은 Xlib를 직접 사용하여 `XNextEvent` 계열 함수를 기반으로 만들어진다. `XNextEvent`는 이벤트 큐에 이벤트가 나타날 때까지 블로킹되며, 응용 프로그램은 이를 적절하게 처리한다. Xlib 이벤트 루프는 윈도 시스템 이벤트만 처리한다. 다른 파일 및 장치에서 대기해야 하는 응용 프로그램은 `ConnectionNumber`와 같은 기본 요소에서 자체 이벤트 루프를 구성할 수 있지만, 실제로 멀티스레딩을 사용하는 경우가 많다.

Xlib를 직접 사용하는 프로그램은 매우 드물다. 일반적으로 Xlib 기반 GUI 툴킷이 이벤트를 추가하는 기능을 제공한다. 예를 들어, Xt Intrinsics 기반 툴킷에는 `XtAppAddInput()` 및 `XtAppAddTimeout()`이 있다.

X 응용 프로그램은 임의의 상태(예: `XNextEvent` 내)에서 중단될 수 있으므로, 시그널 핸들러에서 Xlib 함수를 호출하는 것은 안전하지 않다.[16]

4. 3. GLib 이벤트 루프

GLib 이벤트 루프는 원래 GTK에서 사용하기 위해 만들어졌지만, 현재는 D-Bus와 같은 비GUI 애플리케이션에서도 사용된다. 폴링되는 리소스는 애플리케이션이 관심을 갖는 파일 디스크립터들의 모음이며, 폴링 블록은 신호가 도착하거나 타임아웃이 만료될 경우(예: 애플리케이션이 타임아웃 또는 유휴 작업을 지정한 경우) 중단된다.[17] GLib는 파일 디스크립터 및 자식 종료 이벤트에 대한 내장 지원을 제공하지만, prepare-check-dispatch 모델에서 처리할 수 있는 모든 이벤트에 대해 이벤트 소스를 추가할 수 있다.

GLib 이벤트 루프를 기반으로 구축된 애플리케이션 라이브러리에는 GStreamer 및 GnomeVFS의 비동기 I/O 방식이 포함되지만, GTK가 가장 눈에 띄는 클라이언트 라이브러리로 남아 있다. X의 창 시스템에서 온 이벤트(X 소켓에서 읽음)는 GDK에 의해 GTK 이벤트로 변환되어 애플리케이션의 위젯 객체에서 GLib 신호로 방출된다.

4. 4. macOS Core Foundation run loops

macOS에서는 스레드마다 하나의 CFRunLoop가 있으며, 거기에 임의 개수의 소스(이벤트 소스)와 옵저버(핸들러)를 대응시킬 수 있다.[1] 이 루프가 메시지 큐잉과 디스패치를 수행하며, 이를 통해 소스와 옵저버가 상호 작용한다.[1] 각 스레드당 정확히 하나의 CFRunLoop만 허용되며, 임의의 수많은 소스 및 옵저버를 연결할 수 있다.[2] 그러면 소스는 런 루프를 통해 옵저버와 통신하며, 런 루프는 메시지의 큐잉 및 디스패치를 구성한다.[2]

CFRunLoop는 코코아에서 NSRunLoop로 추상화되어 있으며, 이를 통해 모든 메시지 (비-반사적 런타임에서 함수 호출과 동일)를 모든 객체로 디스패치하기 위해 큐에 넣을 수 있다.[3]

4. 5. 안드로이드 애플리케이션

자바 언어용 안드로이드 SDK의 애플리케이션 프레임워크에는 메시지를 표현하는 android.os.Message 클래스[18], 메시지 큐를 표현하는 android.os.MessageQueue 클래스[19], 메시지 루프의 구현인 android.os.Looper 클래스[20], 메시지의 송신과 처리를 담당하는 android.os.Handler 클래스[21] 등이 준비되어 있다. 단, Looper.loop() 메서드의 구현에 사용되는 MessageQueue.next() 메서드 등이 API로 공개되지 않았기 때문에[22], 메시지 루프를 독자적으로 구현할 수는 없다.

Android NDK에서는, POSIX 파이프와 ALooper 관련 API[23]를 이용하여 네이티브 스레드에 메시지 루프를 독자적으로 구현할 수 있다. NativeActivity의 샘플에는 ALooper를 이용한 메시지 루프의 구현이 포함되어 있다[24].

5. 다른 설계 방식과의 비교

이러한 접근 방식은 여러 다른 대안과 대조된다.


  • 전통적으로 프로그램은 한 번 실행된 후 종료되었다. 이러한 유형의 프로그램은 초창기 컴퓨터에서 매우 흔했으며 어떠한 형태의 사용자 상호 작용도 없었다. 이는 특히 명령줄 기반 프로그램 형태로 여전히 자주 사용된다. 모든 매개변수는 미리 설정되어 프로그램이 시작될 때 한 번에 전달된다.
  • 메뉴 기반 디자인. 이들은 여전히 메인 루프를 특징으로 할 수 있지만, 보통 의미의 이벤트 기반으로 간주되지 않는다. 대신, 사용자는 수행하려는 작업이 유일한 옵션이 될 때까지 점점 좁아지는 옵션 집합을 제시받는다. 메뉴를 통해 제한된 상호 작용이 가능하다.

참조

[1] 웹사이트 Linux_2_6_16 - Linux Kernel Newbies https://kernelnewbie[...] 2021-03-03
[2] 웹사이트 The self-pipe trick http://cr.yp.to/docs[...]
[3] Man pselect Linux
[4] 웹사이트 GetMessage() function https://msdn.microso[...]
[5] 웹사이트 The self-pipe trick http://cr.yp.to/docs[...] 2013-02-05
[6] Man pselect
[7] 문서 Windowsにおける排他制御のためのカーネルオブジェクトのひとつとして「イベント (プログラミング)|イベント」(Win32イベント)があるが、これはWin32メッセージとは関係ない。
[8] 웹사이트 WinMain function (winbase.h) | Microsoft Docs https://docs.microso[...]
[9] 웹사이트 Window Procedures - Windows applications | Microsoft Docs https://docs.microso[...]
[10] 웹사이트 GetMessageW function (winuser.h) | Microsoft Docs https://docs.microso[...]
[11] 웹사이트 PeekMessageW function (winuser.h) | Microsoft Docs https://docs.microso[...]
[12] 웹사이트 GetMessage() function http://msdn.microsof[...]
[13] 웹사이트 Idle Loop Processing | Microsoft Docs https://docs.microso[...]
[14] 웹사이트 Sharing Message Loops Between Win32 and WPF | Microsoft Docs https://docs.microso[...]
[15] 웹사이트 Windows Forms and WPF interop input architecture | Microsoft Docs https://docs.microso[...]
[16] 웹사이트 Chapter: 26 Signal Handling http://www.ist.co.uk[...]
[17] 웹사이트 The Main Event Loop - Customizing the main loop iteration http://developer.gno[...] Gnome Dev Center
[18] 웹사이트 Message | Android Developers https://developer.an[...]
[19] 웹사이트 MessageQueue | Android Developers https://developer.an[...]
[20] 웹사이트 Looper | Android Developers https://developer.an[...]
[21] 웹사이트 Handler | Android Developers https://developer.an[...]
[22] 웹사이트 core/java/android/os/Looper.java - platform/frameworks/base - Git at Google https://android.goog[...]
[23] 웹사이트 Looper | Android NDK | Android Developers https://developer.an[...]
[24] 웹사이트 サンプル: native-activity | Android NDK | Android Developers https://developer.an[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com