임베디드 스터디 - 세마포어, 생산자-소비자, 독자-저자 문제
임베디드 스터디 - 세마포어, 생산자-소비자, 독자-저자 문제
세마포어(Semaphore)란
- 정수 변수
S와 두 가지 원자적 연산으로 구성되는 동기화 도구 P(S)— 감소 + 대기:S--, S < 0이면 호출 스레드를 대기 큐에 블록V(S)— 증가 + 깨우기:S++, S ≤ 0이면 대기 큐에서 스레드 하나를 깨움S가 음수일 때|S|는 대기 중인 스레드 수를 의미한다
1
2
3
4
5
P(S): S--;
if (S < 0) { 대기 큐에 블록 } // 이 지점에서 잠들었다가 깨어남
V(S): S++;
if (S <= 0) { 대기 큐에서 하나 깨움 }
이진 세마포어 vs 계수 세마포어
| 종류 | S 초기값 | 동시 접근 허용 수 | 용도 |
|---|---|---|---|
| 이진 세마포어 | 0 또는 1 | 1개 스레드 | 상호배제(뮤텍스와 유사) |
| 계수 세마포어 | N (N > 1) | N개 스레드 | 자원 개수 제한 |
- S = 1로 초기화하면 임계구역 보호에 사용 가능:
1
2
3
4
5
6
초기: S = 1
T0: P(S) → S = 0, 임계구역 진입
T1: P(S) → S = -1, 대기 큐 블록
T0: V(S) → S = 0, T1 깨움 → T1 임계구역 진입
T1: V(S) → S = 1
생산자-소비자 문제(Producer-Consumer Problem)
- 크기 N인 공유 버퍼를 생산자와 소비자가 함께 사용하는 문제
- 세마포어 3개 사용:
1
2
3
semaphore mutex = 1; // 버퍼 접근 상호배제
semaphore empty = N; // 빈 슬롯 수
semaphore full = 0; // 채워진 슬롯 수
1
2
3
4
5
6
// 생산자 // 소비자
P(empty); P(full); // 채워진 슬롯 확인
P(mutex); P(mutex);
/* 데이터 삽입 */ /* 데이터 꺼냄 */
V(mutex); V(mutex);
V(full); V(empty); // 빈 슬롯 +1
- 데드락 주의: 생산자에서
P(mutex)를P(empty)보다 먼저 호출하면 — 버퍼가 가득 찬 상태에서 mutex를 잡은 채 empty를 기다리므로 데드락 발생
독자-저자 문제(Readers-Writers Problem)
- 핵심 규칙:
- 독자(Reader): 임계영역에 동시에 접근 가능
- 저자(Writer): 혼자만 접근 (독자도 없어야 함)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int read_count = 0;
semaphore mutex = 1; // read_count 보호
semaphore rw_lock = 1; // 저자 접근 제어
// 독자 진입
P(mutex);
read_count++;
if (read_count == 1) P(rw_lock); // 첫 번째 독자만 저자 차단
V(mutex);
/* 읽기 */
// 독자 퇴장
P(mutex);
read_count--;
if (read_count == 0) V(rw_lock); // 마지막 독자만 저자 잠금 해제
V(mutex);
- 첫 번째 독자만 rw_lock을 잡는 이유: 모든 독자가 P(rw_lock)을 호출하면 독자들이 대기하게 되어 동시 읽기가 불가능해진다
- 독자 우선 방식의 단점: 독자가 계속 도착하면 저자는 기아 상태에 빠진다
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.