/

뮤텍스 세마포어를 묻는 면접에서는 선택 근거를 먼저 짧게 제시합니다. 면접에서 둘 중 하나를 고르라면 소유권과 허용 개수(카운트)부터 말하면 됩니다. 한 스레드가 잠그고 같은 스레드가 풀어야 하는 상호배제는 뮤텍스를, 동시에 허용할 작업 수를 세야 하면 세마포어를 우선 검토하고, 실시간 우선순위 조건이 있으면 뮤텍스의 PI 속성을 별도로 확인하는 흐름으로 잡습니다.

이 글은 면접에서 동기화 도구를 고르는 기준을 정리한 일반 안내이며, 운영체제·언어 런타임·커널 버전에 따라 세부 동작은 달라질 수 있습니다.

뮤텍스와 세마포어는 언제 고르나?

한 줄 답: 잠근 주체와 푸는 주체가 같아야 하는 상호배제면 뮤텍스를, 허가 개수를 주고받아야 하면 세마포어를 우선 검토합니다.

선택 순서는 먼저 소유권을 확인하고, 다음으로 동시에 허용할 개수를 확인하는 방식으로 잡습니다. 한 스레드가 잠그고 같은 스레드가 풀어야 하는 상호배제라면 뮤텍스를 우선 검토하고, 허가 수를 주고받는 요구라면 세마포어를 검토합니다.

pthread_mutex_lock(3)의 POSIX 뮤텍스는 unlocked 또는 locked 상태이며, locked 상태에서는 한 스레드가 소유합니다. lock에 성공하면 호출 스레드가 소유자가 되므로 두 스레드가 동시에 소유할 수 없습니다. 따라서 보호 대상의 상호배제와 소유자 기반 해제가 선택 조건이면 뮤텍스를 우선 검토합니다.

unlock은 호출 스레드가 해당 뮤텍스의 소유자라는 전제를 둡니다. error-checking 또는 recursive 종류는 비소유자의 해제를 오류로 처리하며, error-checking에서는 EPERM을 반환하고 뮤텍스 상태를 바꾸지 않습니다. fast 종류에서 소유자 검사가 생략되어 다른 스레드의 해제가 가능할 수 있지만, 이는 비이식적이므로 선택 기준으로 의존하지 않습니다.

C++의 std::mutex도 배타적이고 비재귀적인 소유권을 가집니다. lock 또는 try_lock에 성공한 호출 스레드가 unlock할 때까지 소유하므로, 이미 소유한 상태에서 다시 lock이나 try_lock을 호출하지 않습니다. 잠금 범위는 보통 std::lock_guard, std::unique_lock, std::scoped_lock으로 관리합니다.

C++ jthread는 C++ 스레드·잠금 맥락을 이어서 확인할 때 참고합니다.

다음 표는 특정 회사의 기출이나 출제 경향을 주장하는 표가 아니라, 도구를 고를 때 확인할 일반 기준입니다. 소유권·허용 개수·해제 주체·PI를 같은 순서로 대조하면 선택 근거를 짧게 정리할 수 있습니다.

일반 기준 뮤텍스 세마포어
소유권 성공한 lock의 호출 스레드가 소유합니다. 소유자 대신 허가 수를 다룹니다.
허용 개수 한 스레드의 배타적 소유를 기준으로 봅니다. 0 아래로 내려가지 않는 정수로 허가 개수를 관리합니다.
해제 주체 소유자 해제를 기준으로 봅니다. fast 종류의 다른 스레드 해제는 비이식적입니다. sem_post를 호출하는 주체에 소유자 검사가 없습니다.
PI PTHREAD_PRIO_NONE·INHERIT·PROTECT 설정을 확인합니다. 소유자 개념이 없어 같은 PI 프로토콜을 기본으로 제공하지 않습니다.

바이너리 세마포어와 뮤텍스는 어떻게 다른가?

한 줄 답: 초기값 1인 바이너리 세마포어는 상호배제처럼 보이지만, 소유권과 재귀 잠금 규칙이 필요하면 뮤텍스를 우선 검토합니다.

sem_overview(7)에 따르면 세마포어는 0 아래로 내려가지 않는 정수입니다. sem_post는 값을 1 증가시키고 sem_wait는 1 감소시키며, 값이 0이면 sem_wait는 값이 양수가 될 때까지 블록합니다. named와 unnamed 형태가 있으며, unnamed 세마포어는 sem_init으로 초기화하고 스레드 공유 또는 공유 메모리를 통한 프로세스 공유로 사용할 수 있습니다.

sem_wait는 값이 0보다 크면 즉시 1을 감소시키고 반환합니다. 값이 0이면 감소가 가능해지거나 시그널이 올 때까지 블록합니다. sem_trywait는 즉시 감소할 수 없으면 EAGAIN을 반환하며, 실패하면 세마포어 값은 그대로입니다. 자세한 동작은 sem_wait(3)에서 확인합니다.

초기값 1인 바이너리 세마포어는 한 번에 하나의 허가만 나타내므로 상호배제처럼 보입니다. 그러나 소유권이 없어 잠근 스레드가 아닌 다른 스레드도 sem_post를 호출할 수 있습니다. 같은 스레드가 다시 sem_wait를 호출한다고 재귀 뮤텍스처럼 잠금 횟수가 쌓이지도 않습니다.

sem_post는 값을 1 증가시키고, 결과가 0보다 커지면 sem_wait로 대기하는 다른 프로세스나 스레드를 깨울 수 있습니다. 또한 sem_post(3)는 sem_post가 async-signal-safe이므로 시그널 핸들러에서 호출할 수 있다고 설명합니다. 반대로 pthread_mutex_lock(3)은 pthread 뮤텍스 함수가 async-signal-safe가 아니며 시그널 핸들러에서 lock이나 unlock을 호출하면 데드락이 날 수 있다고 설명합니다.

카운팅 세마포어는 언제 쓰나?

한 줄 답: 동시에 허용할 개수가 2 이상이면 카운팅 세마포어로 허가 수를 관리하는 방식을 우선 검토합니다.

허용 개수가 2 이상인 자원이나 작업을 동시에 다루면 카운팅 세마포어를 우선 검토합니다. 예를 들어 동시에 N개만 사용할 수 있는 풀에서는 초기값을 N으로 두고 허가 수를 관리합니다. 생산자-소비자에서는 빈 칸과 찬 칸의 수를 세는 용도로 활용합니다.

이때 빈 칸·찬 칸 같은 카운트는 세마포어로 관리하고, 공유 버퍼의 포인터 등 공유 구조를 보호하는 임계구역은 뮤텍스로 나누는 설명이 일반적입니다. 카운팅 세마포어 하나로 개수와 공유 구조 보호를 모두 처리한다고 하면 책임이 섞여 레이스가 남을 수 있습니다. 선택 근거는 허가 수 관리와 공유 구조 보호를 분리해 말합니다.

우선순위 역전은 왜 뮤텍스 선택에 들어가나?

한 줄 답: 실시간 스케줄과 우선순위 속성이 실제 조건이면, 대기자의 우선순위를 소유자가 상속하는 뮤텍스 PI 설정을 확인해야 합니다.

실시간 스케줄과 우선순위 조건이 실제 요구사항이면, 뮤텍스를 고를 때 protocol 속성을 별도로 확인합니다. POSIX는 PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT, PTHREAD_PRIO_PROTECT를 구분하며 기본값은 PTHREAD_PRIO_NONE입니다. 이 구분은 pthread_mutexattr_getprotocol(3p)에 정리되어 있습니다.

PTHREAD_PRIO_INHERIT에서는 자신보다 높은 우선순위로 대기하는 스레드가 있으면 뮤텍스 소유 스레드가 그 우선순위를 상속합니다. Linux RT-mutex는 PI-futex를 통해 pthread 뮤텍스의 PTHREAD_PRIO_INHERIT를 지원하며, unlock하면 상속된 우선순위가 즉시 해제됩니다. Linux kernel RT-mutex에서 이 지원을 확인할 수 있습니다.

세마포어는 소유자 개념이 없으므로 같은 PI 프로토콜을 기본으로 제공하지 않습니다. 그렇다고 세마포어가 우선순위 역전을 절대 막을 수 없다고 단정하지 않고, 실제 구현과 설정의 지원 범위를 확인합니다.

면접에서 자주 나오는 함정은 무엇인가?

한 줄 답: 비슷해 보이는 바이너리 동작만 보고 소유권·해제 주체·허용 개수·실시간 조건을 생략하면 선택 근거가 틀어집니다.

바이너리 세마포어와 뮤텍스가 모두 하나의 통과만 허용하는 것처럼 보여도, 소유권·해제 주체·재귀·시그널 안전성은 따로 확인합니다. 다른 스레드가 뮤텍스를 해제해도 된다고 말하는 것은 POSIX fast 뮤텍스의 비이식적 동작에 의존한 설명입니다. 소유자와 해제 주체의 일치가 필요하면 뮤텍스를 우선 검토합니다.

세마포어가 더 일반적이므로 상호배제에 항상 대신 사용할 수 있다고 단정하는 것도 함정입니다. 허용 개수가 핵심이면 세마포어를, 소유권이 필요한 상호배제면 뮤텍스를 검토합니다. 카운팅 세마포어로 공유 구조까지 보호한다고 하면 카운트와 임계구역 보호가 섞여 레이스가 남을 수 있습니다.

Linux 커널 mutex는 바이너리 세마포어와 비슷하게 동작하지만 owner를 추적하며, 한 태스크만 보유하고 소유자만 unlock할 수 있습니다. 다중 unlock과 재귀 lock/unlock을 금지하고, 인터럽트·태스크릿·타이머 컨텍스트에서는 사용하지 않습니다. Linux kernel mutex design은 이런 커널 mutex 규칙을 설명합니다. 커널 잠금 규칙은 사용자 공간의 도구 선택과 다른 층이므로 그대로 일반화하지 않습니다.

FAQ에서 고르는 기준은 무엇을 보면 되나?

한 줄 답: 소유권 필요 여부, 허용 개수, 누가 해제하는지, 실시간 PI 조건의 순서로 확인합니다.

FAQ는 도구 이름보다 상황의 조건을 빠르게 대조하는 용도로 정리합니다. 아래 표의 답은 특정 시험 문제를 주장하지 않는 일반 기준입니다.

상황 고르는 기준
같은 스레드가 풀어야 하면 호출 스레드의 소유권이 필요하므로 뮤텍스를 우선 검토합니다.
동시 허용 수가 여러 개면 허용 개수가 2 이상이면 카운팅 세마포어로 허가 수를 관리하고, 공유 구조 보호는 뮤텍스로 분리합니다.
시그널 핸들러 sem_post는 async-signal-safe이지만 pthread 뮤텍스 함수는 아니므로, 핸들러에서 호출할 연산을 구분합니다.
PI는 언제 실시간 우선순위와 대기 조건이 실제로 있으면 뮤텍스 protocol과 PTHREAD_PRIO_INHERIT 지원을 확인합니다.

관련 글은 무엇을 보면 되나?

한 줄 답: 정의, 프로세스·스레드 선택, C++의 스레드·잠금 맥락, C++ 면접 점검은 각각의 관련 글에서 따로 확인합니다.

+ Recent posts