1960년대 Dijkstra가 설계한 시스템 호출
여러 쓰레드(또는 프로세스) 간의 크리티컬 섹션(공유 자원 접근)을 제어하는 데 사용됨
(기본 특징)
block/wakeup 알고리즘을 통해 상호 배제를 보장함
프로세스가 크리티컬 섹션에 들어갈 수 없을 경우 ➡︎ block됨 (즉시 CPU 반납)
크리티컬 섹션에서 빠져나오는 프로세스는 ➡︎ 대기 중인 프로세스를 깨움 (ready 상태로 만듦)
CPU 시간을 낭비하지 않음
여러 프로세스가 대기 중이라면 ➡︎ waiting queue(대기 큐) 가 필요함
✔️Semaphore는 공유 자원 접근을 블로킹 & 웨이크업 방식으로 제어해서 CPU 낭비 없이 안정적인 동기화를 구현하는 도구
Semaphore S(1); // 초기값 1로 설정된 전역 세마포어

두 프로세스/쓰레드는 동시에 critical section에 들어갈 수 없도록 설계됨.
S.wait() → 자원 확보
S.signal() → 자원 반납
Semaphore Printer(3); // 프린터 3대를 관리하는 세마포어

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

P₁이 먼저 wait 지점에 도달한 경우
Sync.wait()에서 대기 (세마포어가 0이라서)
→ P₀의 signal까지 기다림
P₀이 먼저 signal 지점에 도달한 경우
Sync.signal() 실행 → 세마포어가 1이 되므로
→ P₁이 block 없이 통과 가능
따라서 P₁은 P₀의 signal 이후에만 실행됨 → 순서 제어 가능!
전역 변수 설정
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()도 생략 가능
: sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue, sem_destroy
(함수 원형)

✔️ 반드시 pthread 라이브러리와 함께 링크해야 함
→ 컴파일 시 -pthread 옵션 필요함 (ex: gcc -o prog main.c -pthread)
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem: 세마포어 객체를 초기화할 포인터
pshared:
0 → 현재 프로세스 내에서만 사용
0이 아닌 값 → 여러 프로세스 간 공유 (공유 메모리 영역에 있어야 함)
value: 세마포어의 초기값 (ex: 1이면 binary semaphore처럼 동작)
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 감소하고 통과
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem, int *sval);
sem_post()
세마포어 값을 원자적으로 증가시킴 (하나의 연산 단위로 분리할 수 없는 상태에서 값을 증가시킨다는 의미)
절대 block되지 않으며 signal handler에서도 안전하게 사용 가능
sem_getvalue()
int sem_destroy(sem_t *sem);
sem이 가리키는 세마포어 객체를 제거
사용하던 자원 해제
어떤 쓰레드도 해당 세마포어에서 대기 중이면 안 됨
대기 중인 쓰레드가 있을 때 sem_destroy()를 호출하면 정의되지 않은 동작 발생