시스템프로그래밍_ 10-POSIX Semaphores

안수빈·2025년 6월 14일

시스템프로그래밍

목록 보기
5/7
post-thumbnail

<Semaphore (세마포어)>

  • 1960년대 Dijkstra가 설계한 시스템 호출

  • 여러 쓰레드(또는 프로세스) 간의 크리티컬 섹션(공유 자원 접근)을 제어하는 데 사용됨

(기본 특징)

  • block/wakeup 알고리즘을 통해 상호 배제를 보장함

  • 프로세스가 크리티컬 섹션에 들어갈 수 없을 경우 ➡︎ block됨 (즉시 CPU 반납)

  • 크리티컬 섹션에서 빠져나오는 프로세스는 ➡︎ 대기 중인 프로세스를 깨움 (ready 상태로 만듦)

  • CPU 시간을 낭비하지 않음

  • 여러 프로세스가 대기 중이라면 ➡︎ waiting queue(대기 큐) 가 필요함

✔️Semaphore는 공유 자원 접근을 블로킹 & 웨이크업 방식으로 제어해서 CPU 낭비 없이 안정적인 동기화를 구현하는 도구


<Mutual Exclusion by Semaphore (세마포어에 의한 상호 배제)>

  • binary semaphore 또는 counting semaphore를 이용한 상호 배제
Semaphore S(1);  // 초기값 1로 설정된 전역 세마포어


두 프로세스/쓰레드는 동시에 critical section에 들어갈 수 없도록 설계됨.
S.wait() → 자원 확보
S.signal() → 자원 반납


<Resource Allocation by Counting Semaphore (계수 세마포어로 자원 할당)>

Semaphore Printer(3);  // 프린터 3대를 관리하는 세마포어


세마포어 값이 0이 되면, 다른 프로세스는 대기 상태로 들어감 ➡︎ 자원을 동시에 사용하는 수를 제한


<Synchronization with a Semaphore (세마포어를 이용한 동기화)>

Semaphore Sync(0);  // 초기값이 0인 세마포어

P₁이 먼저 wait 지점에 도달한 경우

  • Sync.wait()에서 대기 (세마포어가 0이라서)

    P₀의 signal까지 기다림

P₀이 먼저 signal 지점에 도달한 경우

  • Sync.signal() 실행 → 세마포어가 1이 되므로

    P₁이 block 없이 통과 가능

따라서 P₁은 P₀의 signal 이후에만 실행됨 → 순서 제어 가능!


<Producer/Consumer with Counting Semaphore (생산자/소비자 문제)>

전역 변수 설정

Semaphore mutex(1), num_buffer(n), num_data(0);
int rear = front = -1;

생산자 프로세스 (Producer)

produce an item;
num_buffer.wait();   // 빈 버퍼 확보
mutex.wait();        // 상호 배제
buffer[rear] = pdata;
rear = (rear + 1) % n;
mutex.signal();
num_data.signal();   // 데이터 추가 알림

소비자 프로세스 (Consumer)

num_data.wait();     // 소비할 데이터 확인
mutex.wait();        // 상호 배제
cdata = buffer[front];
front = (front + 1) % n;
mutex.signal();
num_buffer.signal(); // 버퍼 공간 복구
process an item;
  • count 변수는 세마포어로 대체 가능 (굳이 따로 셀 필요 없음)
  • 생산자/소비자가 각 1명뿐이라면 mutex.wait()/signal()도 생략 가능

<SEMAPHORES (3)>

세마포어에 대해 사용할 수 있는 함수들

: sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue, sem_destroy

(함수 원형)

✔️ 반드시 pthread 라이브러리와 함께 링크해야 함
→ 컴파일 시 -pthread 옵션 필요함 (ex: gcc -o prog main.c -pthread)


<Semaphore Initialization (세마포어 초기화)>

int sem_init(sem_t *sem, int pshared, unsigned int value);

sem: 세마포어 객체를 초기화할 포인터

pshared:

0 → 현재 프로세스 내에서만 사용

0이 아닌 값 → 여러 프로세스 간 공유 (공유 메모리 영역에 있어야 함)

value: 세마포어의 초기값 (ex: 1이면 binary semaphore처럼 동작)


<Semaphore Wait Operations (세마포어 대기 연산)>

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);

sem_wait()

  • 세마포어 값이 0 → 현재 쓰레드는 block 상태로 대기

  • 값이 0이 아니면 → 세마포어 값을 원자적으로 감소하고 통과

sem_trywait()

  • 비차단(non-blocking) 방식의 sem_wait

  • 세마포어 값이 0 → 즉시 EAGAIN 오류 반환

  • 값이 0이 아니면 → 즉시 1 감소하고 통과


<Semaphore Post (Signal) & Getvalue Operations>

int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem, int *sval);

sem_post()

  • 세마포어 값을 원자적으로 증가시킴 (하나의 연산 단위로 분리할 수 없는 상태에서 값을 증가시킨다는 의미)

  • 절대 block되지 않으며 signal handler에서도 안전하게 사용 가능

sem_getvalue()

  • 현재 세마포어 값을 *sval에 저장함

<Semaphore Destruction (세마포어 제거)>

int sem_destroy(sem_t *sem);
  • sem이 가리키는 세마포어 객체를 제거

  • 사용하던 자원 해제

  • 어떤 쓰레드도 해당 세마포어에서 대기 중이면 안 됨

  • 대기 중인 쓰레드가 있을 때 sem_destroy()를 호출하면 정의되지 않은 동작 발생

0개의 댓글