포스트

임베디드 스터디 - 세마포어, 생산자-소비자, 독자-저자 문제

임베디드 스터디 - 세마포어, 생산자-소비자, 독자-저자 문제

세마포어(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 또는 11개 스레드상호배제(뮤텍스와 유사)
계수 세마포어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 라이센스를 따릅니다.