여러 프로세스나 스레드가 동시에 동일한 데이터를 조작할 때, 접근 순서나 타이밍에 따라 결과가 달라지는 상황을 말한다.
데이터의 일관성을 유지하기 위해 프로세스 간의 동기화가 필수적이다.
임계 구역이란 공유 데이터의 일관성을 보장하기 위해 단 하나의 프로세스/스레드만 진입하여 실행 가능한 코드 영역을 의미한다.
상호 배제: 한 프로세스가 임계 구역을 실행 중이면 다른 프로세스는 진입할 수 없다.
구성 요소:
진입 구역 (Entry Section): 임계 구역 진입 허가를 요청하는 부분.
임계 구역 (Critical Section): 공유 데이터를 조작하는 코드.
퇴출 구역 (Exit Section): 임계 구역을 빠져나왔음을 알리는 부분.
나머지 구역 (Remainder Section): 그 외의 코드 영역.
임계 구역 문제를 해결하는 프로토콜은 반드시 다음 조건을 만족해야 한다.
상호 배제 (Mutual Exclusion): P1이 임계 구역에서 실행 중이면 다른 프로세스는 진입할 수 없다.
진행 (Progress): 임계 구역에 있는 프로세스가 없고 진입하려는 프로세스들이 있다면, 나머지 구역에서 실행 중이지 않은 프로세스들만 다음에 누가 들어갈지 결정할 수 있으며, 이 결정은 무한정 연기될 수 없다.
한정된 대기 (Bounded Waiting): 프로세스가 진입 요청을 한 후부터 허용될 때까지 다른 프로세스들이 진입하는 횟수에 제한이 있어야 한다. (기아 상태 방지)
커널 코드 내에서도 경쟁 조건이 발생할 수 있으며, 이를 다루는 두 가지 방식이 존재한다.
| 구분 | 선점형 커널 | 비선점형 커널 |
|---|---|---|
| 특징 | 커널 모드 수행 중에도 CPU를 뺏길 수 있음 | 프로세스가 스스로 반납하거나 대기전까지 독점 |
| 장점 | 응답성이 좋아 실시간(Real time) 시스템에 적합하다. | 설계까 단순하고 경쟁 조건 위험이 적다. |
| 단점 | 설계까 복잡하고 동기화 메커니즘이 필수적이다. | 응답성이 떨어질 수 있다. |
두 개의 프로세스(P(i), P(j))가 있을 때 사용 가능한 고전적인 소프트웨어 기반 해결책이다.
int turn; // 누가 들어갈 차례인가?
bool flag[2]; // 들어갈 준비가 되었는가?
do {
flag[i] = true; // 들어가고 싶어!
turn = j; // 하지만 상대방(j)에게 먼저 양보할게.
while (flag[j] && turn == j); // 상대가 준비됐고 상대 차례라면 기다림
/* CRITICAL SECTION */
flag[i] = false; // 완료 후 깃발 내림
/* REMAINDER SECTION */
} while (true);
참고: 현대 CPU는 성능 향상을 위해 명령어 순서를 변경할 수 있어, 소프트웨어 만으로는 완벽한 동작 보장이 어렵다.
임계 구역에 진입할 수 있을 때까지 무한 루프를 돌며 기다리는 방식(Busy Waiting)이다.
Locking 메커니즘을 사용해 자원을 점유한다.
임계 구역을 나갈 때 unlock 할 수 있다.Sleep 상태로 진입해 CPU를 양보하는 방식(Sleep lock)을 주로 사용한다.정수 값을 사용하여 여러 스레드 간의 공유르를 관리하는 Signaling 메커니즘이다.
Busy-waiting : while 문으로 계속 체크 (스핀락 방식)block-wakeup : 자원이 없으면 프로세스를 대기 큐에 넣고 sleep시킨 뒤, 자원이 생기면 커널이 깨워주는 방식이다.| 비교 항목 | 뮤텍스 (Mutex) | 세마포어 (Semaphore) |
|---|---|---|
| 메커니즘 | Locking (잠금) | Signaling (신호) |
| 소유권 | 락을 건 사람만 해제 가능 | 누구나 Signal을 보내 해제 가능하다. |
| 자원 수 | 단일 자원(1개) | 1개 이상 (Counting 가능) |
| 용도 | 상호 배제 위주 | 상호 배제 + 실행 순서 제어(동기화) |