[WINDOWS VIA C/C++] 09. 커널 오브젝트를 이용한 스레드 동기화
https://www.hanbit.co.kr/store/books/look.php?p_code=B2974835990
제프리 리처의 Windows via C/C++(복간판)
이 책은 윈도우 XP, 윈도우 비스타, 윈도우 서버 2008까지 내용을 포괄한다. 이미 윈도우 10이 출시된 지 오래지만 윈도우의 기본 구조는 변하지 않아 아직까지도 이 책은 윈도우 시스템 프로그래
www.hanbit.co.kr
해당 책을 읽고 학습 목적으로 간단하게 정리한 글입니다.
개요
이전 챕터에서는 유저오브젝트 기반의 스레드 동기화 방법에 대해 알아보았고
이번 챕터에서는 커널오브젝트 기반의 동기화 방법을 알아보자
커널 오브젝트를 사용하기 때문에 커널 모드 전환이 필요하고 이는 CPU 사이클을 많이 소요하는 무거운 작업이다.
대부분의 커널 오브젝트는 동기화를 위해 사용될 수 있는데 이를 위해 signal, non-signal 상태를 가진다.
signaled 상태는 깃발이 올라간 상태, non-signaled는 내려간 상태라고 비유할 수 있다.
대기 함수들
Wait Function을 사용하면 특정 오브젝트가 Singaled 상태가 될 때 까지 해당 함수를 호출한 스레드를 wait상태로 만들 수 있다.
기다리는 오브젝트가 함수 호출 당시 이미 Signaled였다면 wait에 빠지지는 않는다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
WaitForSingleObject 함수(synchapi.h) - Win32 apps
지정된 개체가 신호 상태가 되거나 시간 제한 간격이 경과할 때까지 기다립니다.
learn.microsoft.com
해당 함수를 호출하면 특정 오브젝트가 signaled가 될 때 까지 기다릴 수 있다.
두 번째 인자로 최대 대기할 시간을 지정할 수 있다.
만약 시간이 지난다면 반환 값 DWORD가 WAIT_TIMEOUT이 된다.
시간을 INFINITE로 지정한다면 signaled가 될 때 까지 무한정 기다린다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
WaitForMultipleObjects 함수(synchapi.h) - Win32 apps
지정된 개체 중 하나 또는 전부가 신호 상태이거나 시간 제한 간격이 경과할 때까지 기다립니다.
learn.microsoft.com
WaitForMultipleObjects는 이름 그대로 여러 개의 오브젝트를 기다리는 함수이다.
최대 64개 까지 기다릴 수 있다.
인자에 bWaitAll이 있는데 지정한 오브젝트를 모두 기다릴지 확인하는 인자이다.
만약 false라면 하나라도 signaled가 되면 반환된다.
이 때 WAIT_OBJECT_0 + dwCount-1의 값을 통해 dwCount번째 object가 singaled인지 확인할 수 있다.
성공적인 대기의 부가적인 영향
일부 커널 오브젝트는 성공적으로 대기 함수가 호출되면 오브젝트의 상태가 변경될 수 있다.
manual-reset이 아닌 auto-reset을 사용한다면 이후 다시 object가 non-signaled상태가 될 것이다.
WaitForMultipleObjects 함수는 작업을 원자적으로 수행한다.
따라서 여러 개의 오브젝트를 여러 스레드가 동시에 기다릴 때 순환 참조가 발생해 데드락이 걸리는 상황을 방지한다.
이벤트 커널 오브젝트
커널 오브젝트 중 이벤트는 가장 단순한 구조를 가진다.
usage count, auto or manual reset, signaled or non-signaled을 나타내는 값이 존재한다.
manual reset이라면 해당 자원을 기다리는 모든 스레드가 깨어난다.
auto-reset이라면 그 중 하나만 깨어난다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-createeventa
CreateEventA 함수(synchapi.h) - Win32 apps
명명되거나 명명되지 않은 이벤트 개체를 만들거나 엽니다. (ANSI)
learn.microsoft.com
CreateEvent를 통해 이벤트 객체를 생성할 수 있다.
HANDLE CreateEventA(
[in, optional] LPSECURITY_ATTRIBUTES lpEventAttributes,
[in] BOOL bManualReset,
[in] BOOL bInitialState,
[in, optional] LPCSTR lpName
);
액세스마스크를 지정하고 싶다면 CreateEventEx를 사용한다.
커널 오브젝트이기 때문에 사용 후 CloseHandle 호출이 필요하다.
event 생성 뒤에는 SetEvent를 통해 Event를 Signaled로 만들 수 있으며, ResetEvent를 통해 non-signaled로 만들 수 있다.
대기 타이머 커널 오브젝트
특정 시간 또는 일정한 간격으로 커널 오브젝트를 Signaled로 만드는 오브젝트를 대기 타이머 라고 한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-createwaitabletimerw
CreateWaitableTimerW 함수(synchapi.h) - Win32 apps
대기 가능한 타이머 개체를 만들거나 엽니다.
learn.microsoft.com
CreateWaitableTimer를 통해 호출한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer
SetWaitableTimer 함수(synchapi.h) - Win32 apps
지정된 대기 가능 타이머를 활성화합니다. 기한이 되면 타이머에 신호를 받고 타이머를 설정하는 스레드는 선택적 완료 루틴을 호출합니다.
learn.microsoft.com
SetWaitableTimer를 통해 시간을 설정할 수 있다.
시간, 기간, 완료 루틴 등을 설정할 수 있다.
대기 타이머를 이용하면 APC(Asynchronous Procedure Call) 요청을 스레드의 APC 큐에 삽입할 수 있다.
(현재는 IO Completion Port를 많이 사용함)
세마포어 커널 오브젝트
세마포어는 resource count가 있는 뮤텍스와 비슷하다.
리소스 카운트가 0보다 크면 semaprhoe는 signaled이다.
0이면 non-signaled이다.
resource count는 음수일 수 없다.
resource count는 최대 resource count보다 커질 수 없다.
CreateSemaphore를 통해 생성 가능하다.
뮤텍스 커널 오브젝트
이를 사용하면 단일 리소스에 대한 스레드의 exclusive한 접근을 보장한다.
user mode의 critical section과 유사하다.
https://learn.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-createmutexa
CreateMutexA 함수(synchapi.h) - Win32 apps
명명되거나 명명되지 않은 뮤텍스 개체를 만들거나 엽니다. (ANSI)
learn.microsoft.com
뮤텍스는 내부적으로 해당 오브젝트를 소유한 스레드의 id를 기억한다.
만약 같은 스레드가 재귀적으로 뮤텍스를 대기하려고 한다면 id를 확인하고 동일하다면 뮤텍스의 반복 카운터가 증가한다.