278. 이진 세마포어 vs 뮤텍스 차이 (소유권 유무)

⚠️ 이 문서는 운영체제의 가장 대표적인 두 가지 동기화 도구인 '이진 세마포어(Binary Semaphore)'와 '뮤텍스(Mutex)'가 겉보기에는 똑같이 0과 1만 가지는 자물쇠처럼 보이지만, 그 내부 설계 철학과 용도가 어떻게 완벽하게 다른지 결정짓는 핵심 개념인 **'소유권(Ownership)'**을 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: 뮤텍스(Mutex)는 자물쇠를 걸어잠근 당사자(스레드)만이 자물쇠를 열 수 있는 **'소유권(Ownership)이 있는 자물쇠'**인 반면, 이진 세마포어는 A 스레드가 잠가둔 것을 엉뚱한 B 스레드가 풀어버릴 수 있는 **'소유권 없는 단순 신호등'**이다.
  2. 가치: 뮤텍스는 오직 단 하나의 스레드만 공유 변수를 안전하게 수정하도록 보호하는 '상호 배제(Mutual Exclusion)'의 목적으로 쓰이며, 세마포어는 스레드 간에 "내 작업 끝났으니 이제 네가 작업해!"라고 순서를 맞춰주는 '실행 순서 동기화(Signaling)' 목적으로 쓰인다.
  3. 융합: 이 둘을 헷갈려서 소유권 보호가 필요한 곳에 이진 세마포어를 쓰면, 해커나 버그가 걸린 스레드가 임의로 자물쇠를 풀어버리는 치명적인 권한 붕괴가 발생하므로, 용도에 맞게 철저히 분리해서 사용해야 한다.

Ⅰ. 개요: 똑같이 생긴 쌍둥이의 함정 (Context & Necessity)

"뮤텍스(Mutex)는 1명만 들어갈 수 있고, 세마포어(Semaphore)는 카운터만큼 여러 명이 들어갈 수 있다." 운영체제를 배우는 학생들이 가장 먼저 외우는 공식이다. 그런데 교수님이 이런 질문을 던진다. "그럼 카운터를 1로 설정한 '이진 세마포어(Binary Semaphore)'와 '뮤텍스'는 완전히 똑같은 거 아니냐?"

상태가 0(잠김)과 1(열림)만 존재한다는 동작 원리 자체는 완전히 똑같다. 둘 다 1명이 들어가면 문이 잠기고, 나오면 문이 열린다. 하지만 이 둘을 실무 코드에서 섞어 쓰는 순간 시스템은 끔찍한 버그에 시달리게 된다. 왜냐하면 뮤텍스는 화장실의 **'자물쇠'**이고, 이진 세마포어는 기차역의 **'신호등'**이라는 전혀 다른 철학으로 만들어진 도구이기 때문이다. 이 차이를 가르는 단어는 바로 **소유권(Ownership)**이다.

📢 섹션 요약 비유: 화장실에 들어가서 안에서 문을 잠갔습니다. 밖에서 기다리는 사람이 밖에서 억지로 문을 열어버릴 수 있나요? 절대 안 되죠! 자물쇠를 잠근 당사자만 열 수 있는 것이 뮤텍스입니다. 반면, 달리기 계주에서 1번 주자가 뛰고 나서 2번 주자에게 바통을 넘기는 것처럼, A가 잠그고(Wait) B가 풀어주는(Signal) 신호 전달 도구가 이진 세마포어입니다.


Ⅱ. 소유권(Ownership)의 차이 심층 분석

1. 뮤텍스 (Mutex: Mutual Exclusion)

  • 핵심 철학: "내가 잠갔으니, 푸는 것도 나만 풀 수 있다."
  • 작동 원리:
    • 스레드 A가 공유 변수에 접근하기 위해 lock()을 건다.
    • 이 순간 OS는 이 뮤텍스의 '소유자(Owner)'가 스레드 A임을 명부(TCB)에 기록해 버린다.
    • 만약 스레드 B가 미쳐서 unlock()을 호출하며 강제로 자물쇠를 풀려고 시도하면? OS는 "넌 주인이 아니잖아!" 라며 가차 없이 에러(IllegalMonitorStateException 등)를 뱉고 튕겨낸다.
  • 추가 기능: 소유권이 있기 때문에, 똑똑한 OS는 A가 락을 쥔 상태에서 죽어버리면 락을 강제로 회수해서 문을 따주는 우선순위 역전 방지 (Priority Inheritance) 같은 고오급 기술을 뮤텍스에만 적용해 줄 수 있다.

2. 이진 세마포어 (Binary Semaphore)

  • 핵심 철학: "누가 잠그든 누가 열든 상관없어. 0과 1 상태만 바뀌면 돼."
  • 작동 원리:
    • 스레드 A가 P() 연산을 호출하여 세마포어를 0으로 만들고(잠금) 잠에 빠진다. (예: A는 파일 다운로드를 기다리는 중)
    • 저 멀리서 다운로드를 끝낸 스레드 B가 V() 연산을 호출하여 세마포어를 1로 풀어버린다!
    • A가 깨어나서 다음 작업을 진행한다.
  • 즉, 세마포어는 자물쇠가 아니라 "나 끝났어! 너 시작해!"를 알려주는 알람 벨(Signaling) 도구다. 소유자가 누군지 OS는 관심도 없으므로, 버그가 난 코드(스레드 C)가 맘대로 V()를 계속 호출해 버리면 시스템의 동기화가 완벽히 무너진다.
┌────────────────────────────────────────────────────────────────────────────┐
│           뮤텍스와 이진 세마포어의 소유권 제어 비교 시각화                 │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 🛡️ [ 뮤텍스 (Mutex) : 화장실 자물쇠 모드 ]                                 │
│   [스레드 A] ───(Lock 잠금)───▶ 화장실 사용 중 (A가 소유자)                │
│                                                                            │
│   [스레드 B] ───(Unlock 풀기 시도!)──▶ ❌ [ OS: "너 A 아니잖아! 에러!" ]   │
│   [스레드 A] ───(Unlock 풀기)───▶ ⭕ 화장실 문 열림. 정상.                 │
│                                                                            │
│                                                                            │
│ 🚦 [ 이진 세마포어 (Binary Semaphore) : 릴레이 바통 모드 ]                 │
│   [스레드 A (화면그리기)] ──(P 감소)──▶ "사진 다운로드 될때까지 잔다 Zzz"  │
│                                                                            │
│   [스레드 B (다운로드팀)] ──(V 증가)──▶ "나 일 끝났어! 락 풀어줄게 🔔"     │
│   [스레드 A (화면그리기)] ◀──(깨어남)── "오! 풀렸네. 이제 화면 그려야지!"  │
│                                                                            │
│   * 결론: 세마포어는 A가 잠그고 B가 풀 수 있다! 소유권이 아예 없다!        │
└────────────────────────────────────────────────────────────────────────────┘

[다이어그램 해설] 이 차이는 개발자에게 엄청난 자유도와 동시에 책임을 부여한다. 공유 데이터(예: 은행 계좌)가 동시에 수정되는 것을 막아야 할 때는 무조건 뮤텍스를 써야 한다. 반면, 음악 재생 스레드가 네트워크 다운로드 스레드를 "기다려야(Wait)" 할 때는 자물쇠가 필요한 게 아니라 알람이 필요한 것이므로 세마포어를 써야 한다.


Ⅲ. 면접과 실무의 단골 함정 (우선순위 역전 현상)

화성 탐사선 패스파인더(Pathfinder)가 화성 표면에서 갑자기 리부팅되며 멈춰버린 유명한 사건이 있다. 원인은 바로 **'우선순위 역전(Priority Inversion)'**이었다.

  • 상황: 중요도가 낮은 스레드 L이 락을 쥐고 있고, 중요도가 최고인 스레드 H가 그 락을 기다린다. 중간 중요도 M이 L을 스케줄링에서 밀어내고 영원히 CPU를 차지해 버리면, 최고 중요도 H는 영원히 실행되지 못해 시스템이 죽는다.
  • 뮤텍스의 구원: 뮤텍스는 **'소유권'**이 있으므로 OS가 "오! H가 L을 기다리네? L의 중요도를 H만큼 임시로 뻥튀기시켜 줄게! 빨리 락 쓰고 내놔!(Priority Inheritance)" 라고 도와줄 수 있다. 화성 탐사선은 이 기능으로 부활했다.
  • 세마포어의 비극: 세마포어는 소유권 개념이 아예 없기 때문에, OS는 누가 누굴 기다리는지 파악조차 못한다. 따라서 세마포어를 상호 배제(공유 자원 보호) 용도로 쓰다가 우선순위 역전이 터지면 답이 없다.

Ⅳ. 결론

"망치를 자물쇠로 쓰지 마라." 뮤텍스와 이진 세마포어는 내부의 수학적 구현(0과 1)이 같을 뿐, OS가 프로세스를 대하는 관점에서는 전혀 다른 세계의 물건이다. 공유 자원을 망가뜨리지 않게 보호(Protect)하고 싶다면 철저하게 소유권이 증명되는 뮤텍스를 써야 하며, 프로세스 간의 실행 흐름(순서)을 조율하고 알람(Signal)을 주고받고 싶다면 세마포어나 조건 변수(Condition Variable)를 써야 한다. 이 철학을 거스르는 코드는 결국 최악의 동기화 버그로 되돌아올 것이다.


📌 관련 개념 맵

  • 공통 목적: 다중 스레드/프로세스의 동기화 (Synchronization)
  • 뮤텍스의 목적: 상호 배제 (Mutual Exclusion), 임계 구역 보호
  • 세마포어의 목적: 스레드 실행 순서 제어 (Signaling), 자원 개수 카운팅
  • 치명적 차이점: 소유권(Ownership) 유무, 우선순위 역전 방지 프로토콜(PIP) 적용 가능 여부

👶 어린이를 위한 3줄 비유 설명

  1. 뮤텍스는 내 일기장에 채워둔 **'자물쇠'**예요. 내가 열쇠로 잠갔으니까, 세상에서 나 혼자만 다시 열쇠를 넣고 열 수 있어요. (소유권 O)
  2. 이진 세마포어는 수영장 화장실의 **'사용 중 팻말(신호등)'**이에요. 내가 안에 들어가면서 팻말을 '사용 중'으로 돌렸지만, 장난꾸러기 친구가 밖에서 맘대로 '비었음'으로 돌려버릴 수 있죠. (소유권 X)
  3. 그래서 내 일기장(중요한 데이터)을 지킬 때는 무조건 자물쇠(뮤텍스)를 써야 하고, 릴레이 달리기할 때 친구에게 바통(신호)을 넘길 때는 팻말(세마포어)을 써야 한답니다!