비선점 (No Preemption)
핵심 인사이트 (3줄 요약)
- 본질: 비선점 (No Preemption)은 교착 상태(Deadlock)를 발생시키는 4대 필요조건 중 하나로, 프로세스가 쥐고 있는 자원(Lock)을 다른 프로세스나 운영체제가 강제로 빼앗을 수 없고 오직 스스로 반납할 때까지 기다려야만 하는 규칙을 뜻한다.
- 가치: 데이터의 무결성(Integrity)을 지키기 위해서는 임계 구역 내의 작업 도중 락이 뜯겨나가는 것을 막아야 하므로 "비선점"은 동기화의 1원칙인 '상호 배제'를 지탱하는 절대적 권리다.
- 융합: 하지만 이 불가침의 권리가 데드락의 원흉이 되므로, 실무에서는 데드락 감지 시 희생자(Victim)를 골라 락을 강제로 뺏어버리거나(DB Rollback), 락을 얻지 못하면 내가 쥐고 있던 락마저 모두 자진 반납(선점 허용 효과)하는 로직으로 융합/타파된다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
- 개념: 한 프로세스가 할당받은 자원(Mutex, Semaphore 등)을 스스로 다 썼다고 선언(Unlock)하기 전까지는, 그 누구도(심지어 최고 권한을 가진 커널 스케줄러라도) 그 자원을 강제로 회수해 갈 수 없는 상태를 말한다.
- 필요성: 은행 계좌에서 돈을 빼고 더하는 연산을 하고 있는데, 갑자기 경찰(OS)이 나타나서 "급한 놈이 있으니 네가 쓰던 도마(메모리) 뺏어갈게" 하고 뺏어가면 돈 계산이 반 토막 난 채로 허공에 뜬다. 데이터가 깨지는 것을 막으려면 "내가 자물쇠를 풀기 전까진 절대 건드리지 마!"라는 절대적인 비선점 특권이 반드시 필요하다.
- 💡 비유: 공중화장실에 들어가서 문을 잠갔을 때, 밖에서 기다리는 사람이 대통령이든 깡패든 '내가 바지를 올리고 스스로 문을 열고 나오기 전까지는 절대 밖에서 문을 부수고 들어올 수 없는' 철통 같은 프라이버시 보호법과 같다.
- 등장 배경: 상호 배제(Mutual Exclusion)를 달성하기 위해 자물쇠를 만들었더니, 이 자물쇠가 너무 튼튼한 나머지 데드락(Deadlock)이 터졌다. 에드워드 코프만이 데드락을 분석해 보니, "아무리 꼬여도 결국 문을 부수고(선점) 들어가면 데드락이 풀리네? 그런데 못 부수니까(비선점) 데드락이 유지되는구나"라고 깨닫고 이를 4대 필요조건의 하나로 명시했다.
[비선점(No Preemption)이 교착 상태(Deadlock)를 유지하는 메커니즘]
[ 스레드 A ] [ 스레드 B ]
1. Mutex_1 획득 1. Mutex_2 획득
2. Mutex_2 요청 ─▶ 대기 2. Mutex_1 요청 ─▶ 대기
[ OS 커널의 딜레마 (비선점 원칙 앞에서의 무력함) ]
- 커널: "얘들아, 이러다 다 죽어! A야, 네가 쥐고 있는 Mutex_1 잠깐만 뺏을게!"
- A: "안돼! 나 아직 계산 중이야. 중간에 뺏기면 데이터 박살나!" (비선점 권리 행사)
- B: "나도 Mutex_2 절대 안 놔줘!"
🚨 결과: 누구도 강제로 뺏을 수 없고(No Preemption),
누구도 스스로 놓지 않으므로(Hold) 시스템은 무한 정지(Deadlock) 상태로 굳어짐.
[다이어그램 해설] "문맥 교환(Context Switch)의 선점"과 헷갈리면 안 된다. CPU는 선점당해서 다른 놈이 연산할 수 있다. 하지만 **"자원(Lock)"**은 선점당하지 않는다. A가 CPU를 뺏겨 대기실로 쫓겨날 때도 Mutex_1이라는 자물쇠는 A의 주머니 속에 그대로 들어있다. 이것이 비선점의 핵심이다.
- 📢 섹션 요약 비유: 수술실에서 의사가 메스를 쥐고 수술 중입니다. 바깥에서 아무리 급한 환자가 와도 수술 중인 의사의 손에서 메스를 강제로 뺏으면(선점) 환자 배를 가른 채로 죽게 됩니다. 그래서 병원(OS)은 수술이 끝날 때까지 메스(자원)를 절대 뺏을 수 없는 비선점 규칙을 지켜야만 합니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
비선점(No Preemption) 조건을 파괴하는 예방(Prevention) 설계
데드락을 막기 위해 4가지 조건 중 하나를 깨야 할 때, 비선점(No Preemption) 조건을 깨부수는 아키텍처적 접근은 아주 과격하고 치명적이다.
1. 자발적 반납 (Voluntary Preemption)
- 로직: 스레드 A가 자원 1을 쥐고 있는 상태에서 자원 2를 달라고 요청했는데 거절당했다(누가 쓰고 있다).
- 파괴 동작: A는 멍청하게 기다리지 않는다. 자원 2를 못 얻으면, "내가 쥐고 있던 자원 1마저도 스스로 토해내고(반납)" 빈손으로 대기 큐의 맨 뒤로 굴러 떨어진다.
- 효과: A가 1을 토해냈으므로, 1을 기다리던 B가 1을 먹고 살아서 데드락이 스르륵 풀린다. (사실상 선점을 허용한 것과 같은 효과).
- 치명적 문제점: CPU 레지스터나 단순 DB 커넥션 같은 건 토해내도 다시 잡으면 그만이지만, 파일이나 테이프 드라이브에 절반쯤 쓰다가 뺏기면(반납하면) 데이터를 처음부터 다시 써야 하거나 파일이 쓰레기가 된다. 따라서 쉽게 쓸 수 있는 기법이 아니다.
2. 강제 뺏기 (Forced Preemption / Rollback)
-
로직: 스케줄러가 데드락을 감지하면, 중요도가 낮은 스레드 하나를 골라 총으로 쏴 죽인다(Kill).
-
파괴 동작: 스레드가 죽으면서 쥐고 있던 락이 바닥에 떨어지므로(강제 선점), 다른 스레드가 그 락을 줍고 살아난다.
-
치명적 문제점: 죽은 스레드가 쓰던 데이터를 롤백(Rollback)해야 한다. DB는 롤백 로그(Undo Log)가 있어서 가능하지만, 일반 C/C++ 프로그램 메모리에서 중간에 뻗은 데이터를 롤백하는 건 불가능에 가깝다.
-
📢 섹션 요약 비유: 비선점 룰을 깨는 것은 "식사 중인 손님의 밥그릇을 빼앗는 행위"입니다. 그냥 빼앗으면 손님이 화를 냅니다(데이터 파괴). 빼앗으려면 "지금까지 먹은 밥값은 안 받을 테니 다음에 다시 처음부터 드세요"라는 롤백(Rollback) 보상 시스템이 반드시 있어야만 시스템이 망가지지 않습니다.
Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)
비선점 (No Preemption) vs 상호 배제 (Mutual Exclusion)
두 가지 모두 데드락의 원인이지만 보호하려는 관점이 다르다.
| 특성 | 상호 배제 (Mutual Exclusion) | 비선점 (No Preemption) |
|---|---|---|
| 보호 대상 | 공유 자원의 '공간(Space)' | 프로세스의 '시간적 주권(Time & Ownership)' |
| 개념 | "내가 방에 들어가면 넌 못 들어와" | "내가 방에 들어간 이상, 네가 날 강제로 끌어낼 순 없어" |
| 데드락 타파 난이도 | 불가능 (공유를 허용하면 데이터가 깨짐) | 조건부 가능 (롤백 기술이나 타임아웃을 쓰면 뺏을 수 있음) |
| 하드웨어 매핑 | TestAndSet의 락킹(Locking) 자체 | 락킹된 객체를 OS가 뺏지 못하게 막는 커널 정책 |
CPU 선점(Preemption)과 자원 비선점(No Preemption)의 엇갈림
가장 헷갈리기 쉬운 개념이다.
-
운영체제는 **"선점형 스케줄링(Preemptive Scheduling)"**을 쓴다. 즉, P1이 돌고 있어도 타이머가 끝나면 P1을 강제로 내쫓고 P2에게 CPU 코어를 줄 수 있다.
-
하지만 P1이 쫓겨날 때, P1이 주머니에 쥐고 있던 **"파일 락(Mutex)"**은 P2에게 뺏기지 않는다. (자원의 비선점).
-
그래서 P2가 CPU를 잡아봤자 파일 락이 없어서 진행을 못 하고, P1도 CPU가 없어서 락을 못 푸는 데드락 지옥이 펼쳐지는 것이다.
-
📢 섹션 요약 비유: 회사에서 사장님(OS)은 내 자리(CPU)를 뺏고 다른 직원을 앉힐 순 있습니다(선점형 스케줄러). 하지만 내가 주머니에 넣고 퇴근한 회사 금고 열쇠(자원)는 사장님이라도 뺏을 수 없습니다(비선점). 다음날 내가 출근해서 금고를 열어줄 때까지 회사는 마비됩니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오
- RDBMS의 데드락 탐지기 (Deadlock Detector): MySQL(InnoDB) 같은 DB 엔진은 OS와 달리 데드락을 가만히 두지 않는다.
- DB는 트랜잭션이 꼬여 데드락이 터지면, '비선점(No Preemption)' 조건을 강제로 부숴버린다.
- 실무 동작: 두 트랜잭션 중 더 가벼운 놈(Undo 로그가 적은 놈)을 **Victim(희생자)**으로 선정하고, 그 트랜잭션을 강제로
KILL시켜버린다(강제 선점). 희생자가 쥐고 있던 락은 해제되고, 살아남은 놈은 락을 얻어 커밋된다. DB는 롤백 기술이 완벽하기에 가능한 극단적 아키텍처다.
- Java / C#의
tryLock()을 통한 비선점 룰의 자발적 파기: 애플리케이션 레벨에서는 남의 락을 뺏을(Kill) 권한이 없다. 따라서 "내가 가진 걸 스스로 놓는" 기법을 쓴다.- 아키텍트 결단: 개발자는
lock.lock()대신lock.tryLock(3 seconds)를 쓴다. 3초간 기다려보고 남의 락을 못 얻으면?finally구문을 태워서 "내가 쥐고 있던 락마저 스스로 언락(Unlock)" 해버린다. (자발적 선점 허용). 이를 통해 시스템은 데드락의 늪에 빠지지 않고 자연스럽게 호흡(Retry)하게 된다.
- 아키텍트 결단: 개발자는
┌─────────────────────────────────────────────────────────────────────┐
│ 교착 상태 4대 조건 중 '비선점' 파괴를 통한 백엔드 아키텍처 설계 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [요구사항: 스레드 A가 자원 1과 자원 2를 모두 얻어야 통과됨] │
│ │ │
│ ▼ 스레드 A의 동기화 로직 전개 │
│ 1. 자원 1 획득 (성공! Hold 상태 진입) │
│ 2. 자원 2 획득 시도 (실패! 다른 놈이 쥐고 있음) │
│ │ │
│ ▼ "비선점(No Preemption)"을 어떻게 깰 것인가? │
│ [ ❌ 무식한 대기 (Hold & Wait 유지) ] │
│ ├─▶ 동작: 자원 1을 쥔 채 자원 2가 풀릴 때까지 영원히 대기. │
│ └─▶ 결과: 상대방도 자원 1을 기다리고 있다면 데드락 폭발! │
│ │
│ [ ✅ 스마트한 롤백 (자발적 비선점 포기) ] │
│ ├─▶ 동작: 자원 2를 못 얻었으니, 즉시 자원 1을 Unlock! │
│ ├─▶ 효과: 쥐고 있던 자원을 방출(선점 허용)하여 데드락 분쇄. │
│ └─▶ 후속: 1초 대기 후 (Backoff) 처음부터 다시 시도(Retry). │
└─────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 클라우드 백엔드나 MSA 환경에서 데드락을 피하는 가장 세련된 방법이 바로 하단의 '백오프 앤 리트라이(Backoff & Retry)' 패턴이다. 이 패턴의 본질은 코프만의 3번 조건(비선점)과 2번 조건(점유 대기)을 프로그래머가 스스로 파괴하여 시스템의 혈을 뚫어주는 데 있다.
- 📢 섹션 요약 비유: 뽑기 기계에서 인형 2개를 뽑아야 성공입니다. 1개를 뽑았는데 2번째 인형이 다른 집게랑 엉켰습니다. 이때 1번 인형을 꽉 쥐고(비선점) 1시간을 멈춰있는 게 아니라, 그냥 1번 인형을 툭 놔버리고(자발적 포기) 처음부터 다시 동전을 넣고 뽑는 것이 가장 빨리 게임을 끝내는 비법입니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
기대효과
비선점 조건을 시스템 설계에서 의도적으로 완화(타임아웃, 롤백, 트랜잭션 킬)하면, 영원히 멈추는 데드락(Deadlock) 대신 잠시 버벅거리다 재시도하는 라이브락(Livelock)이나 지연(Latency) 형태로 장애가 다운그레이드되어, 시스템의 '100% 무중단 가용성(Availability)'을 확보할 수 있다.
결론 및 미래 전망
비선점(No Preemption)은 락(Lock)을 걸어 데이터를 보호하는 폰 노이만 아키텍처 동기화의 뼈대다. 이 권리를 포기하는 것은 롤백 코드를 짜야 하는 엄청난 오버헤드를 동반하므로 운영체제 커널(OS)은 이 조건을 깨는 것을 포기했다. 그러나 미래의 인프라는 OS가 아닌 **데이터베이스(DB)와 분산 메시지 큐(Kafka)**가 중심이 된다. 이들은 내부적으로 트랜잭션 로그(Undo Log)와 보상 트랜잭션(Compensation)이라는 무기를 통해 완벽한 롤백 환경을 구축해 두었다. 따라서 미래의 동시성 제어는 락을 무식하게 쥐고 버티는(비선점) 낡은 코딩에서 벗어나, 언제든 락을 뺏기고(선점당하고) 뒤로 돌아가 재시도(Retry)하는 유연한 낙관적 동시성 제어(Optimistic Concurrency Control, OCC) 패러다임으로 완벽히 이동하고 있다.
- 📢 섹션 요약 비유: 옛날 은행은 도둑이 들면 셔터를 내리고(비선점 데드락) 경찰이 올 때까지 며칠씩 문을 닫았습니다. 현대의 은행은 도둑이 들면 일단 돈을 내어주고(선점 허용), 나중에 CCTV와 보험(트랜잭션 롤백)으로 피해를 완벽히 복구하며 은행 문은 계속 열어두는 유연한 비즈니스로 진화했습니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 교착 상태 (Deadlock) | 비선점 권리가 너무 철저히 지켜질 때 터지는, 시스템 전체가 얼어붙는 최악의 질병이다. |
| 점유 대기 (Hold and Wait) | 비선점과 찰떡궁합을 이루는 데드락의 형제 조건. 내 걸 안 뺏기니까(비선점), 남의 걸 달라고 뻗대는(점유대기) 알박기가 가능해진다. |
| 낙관적 락 (Optimistic Lock) | 비선점의 억압을 뚫기 위해 "일단 락 없이 수정해 보고, 남이 건드렸으면 쿨하게 롤백하자"는 버전 관리 동기화 기술이다. |
| 선점형 스케줄링 (Preemptive) | CPU 코어의 사용권을 강제로 뺏는 기술. 하지만 CPU를 뺏어도 락(Mutex)은 못 뺏기 때문에 데드락은 여전히 발생한다. |
| 상호 배제 (Mutual Exclusion) | 비선점 권리가 지켜주고자 하는 궁극의 목표로, 한 공간에 두 명이 못 들어오게 막는 근본 룰이다. |
👶 어린이를 위한 3줄 비유 설명
- 내가 놀이터에서 그네를 타고 있을 때, 아무리 덩치 큰 형이 와도 내가 스스로 내리기 전까진 억지로 나를 끌어내릴 수 없어요. 이 강력한 권리를 **비선점(No Preemption)**이라고 해요.
- 하지만 이 권리 때문에, 내가 그네에 앉은 채로 옆에 있는 미끄럼틀까지 타겠다고 고집을 부리면, 다른 친구들은 아무도 놀이기구를 탈 수 없이 멈춰버려요(교착 상태).
- 이럴 땐 차라리 내가 쿨하게 그네에서 내려와서(자발적 권리 포기), 다른 친구가 타게 해준 뒤에 나중에 다시 타는 게 모두가 행복하게 노는 똑똑한 방법이랍니다!