세마포어
공유 자원에 대한 접근을 조절하기 위한 동기화 도구
세마포어는 초기값에 의해 동작이 결정되므로 가장 먼저 초기화를 해야한다.
세마포어는 정수 값을 갖는 객체로서 두 개의 함수로 조작
POSIX 표준의 두 개 함수 // 두 함수는 원자적으로 실행
1. sem_wait()
2. sem_post()
#include<semaphore.h>
sem_t s;
sem_init(&s, 0, 1);
세마포어 초기화
세마포어 s를 선언한 후, 세 번째 인자로 1을 전달하여 세마포어 값을 1로 초기화한다.
두 번째 인자는 OSTEP의 모든 예제에서 0
이 값이 0인 경우 같은 프로세스 내의 쓰레드 간에 세마포어를 공유한다는 것을 의미
** 초기화 후 sem_wait(), sem_post() 함수들을 호출하여 세마포어를 다룰 수 있다.
두 루틴은 다수 쓰레드들에 의해 동시에 호출되는 것을 가정
sem_wait()
1. 세마포어 값이 1인 경우, 즉시 리턴
2. 세마포어 값이 1 이상이 될 때까지 호출자를 대기
다수의 쓰레드들이 sem_wait()를 호출할 수 있기 때문에 대기큐에는 다수의 쓰레드가 존재할 수 있다.
대기하는 법에는 회전과 재우기 두 가지가 있다.
sem_post()
세마포어 값을 증가시키고 대기 중인 쓰레드 중 하나를 깨운다.
세마포어 값 음수
세마포어 음수값의 절대값은 현재 대기 중인 쓰레드의 개수와 같다
Ex) -2인 경우 2개의 스레드가 대기 중임
이진 세마포어 - 세마포어를 락처럼 사용
락은 두 개의 상태만 존재하므로 이진 세마포어라고도 불린다.
세마포어 값을 1로 초기화
스레드 0이 sem_wait()를 호출하여 세마포어 값을 0으로 만들고 리턴
스레드 0은 임계 영역에 진입할 수 있음
스레드 0이 임계 영역 내에 있을 때 다른 스레드가 락을 획득하려고 하지 않는다면
sem_post()를 불렀을 때 세마포어 값이 다시 1로 설정된다.
스레드 0이 임계 영역 내에 있을 때 스레드 1이 sem_wait()를 호출하여
임계 영역 진입을 시도하는 경우 스레드 1이 세마포어를 -1로 감소시키고 대기에 들어간다.
(프로세서를 내어주고 잠자기에 들어감)
스레드 0가 다시 실행되고 sem_post()를 호출하면, 세마포어 값을 0으로 증가시키고,
잠자던 스레드 1을 깨운다. 그러면 스레드 1이 락을 획득할 수 있게 된다.
이후 스레드 1이 작업을 마치면 sem_post()를 호출하여 세마포어 값을 증가시킨다.
컨디션 변수로서의 세마포어
어떤 조건이 참이 되기를 기다리기 위해 현재 쓰레드를 멈출 때에도 세마포어는 유용하게 사용됨
쓰레드가 어떤 사건의 발생을 기다리고, 또 다른 쓰레드는 해당 사건을 발생시킨 후,
시그널을 보내어 기다리는 쓰레드를 깨우는 것
대기 중인 쓰레드가 프로그램에서 어떤 조건이 만족되기를 대기하므로
세마포어를 컨디션 변수처럼 사용할 수 있다.
** 세마포어 값 0으로 초기화
sem_t s;
void *
child(void *arg) {
printf("child\n");
sem_post(&s); // 시그널 전달: 자식의 동작이 끝남
return NULL;
}
int
main(int argc, char *argv[]) {
sem_init(&s, 0, X); // X의 값은 무엇이 되어야 할까 ?
printf("parent: begin\n");
pthread_t c;
pthread_create(c, NULL, child, NULL);
sem_wait(&s); // 자식 여기서 대기
printf("parent: end\n");
return 0;
}
어떻게 컨디션 변수로서의 세마포어 효과를 낼 수 있을까 ??
부모 프로세스는 자식 프로세스를 생성 후 sem_wait()를 호출하여 자식의 종료를 대기한다.
자식 프로세스는 sem_post()를 호출하여 종료되었음을 알린다.
그러므로 세마포어의 초기값이 0으로 초기화되어야 한다.
두 가지 상황 발생
1. 자식 프로세스 생성 후, 아직 자식 프로세스가 실행을 시작하지 않은 경우 (준비 큐에만 있는 상태)
이 경우 자식이 sem_post()를 호출하기 전에 부모가 sem_wait()를 호출하게 되므로
부모 프로세스는 자식 프로세스가 실행될 때까지 대기해야 한다.
이를 위해 wait() 호출 전에 세마포어 값이 0보다 같거나 작아야 한다.
1) 부모가 실행되면 세마포어 값을 감소시키고(-1) 대기한다.
2) 자식이 실행되었을 때 sem_post()를 호출하여 세마포어 값을 0으로 증가시킨 후 부모를 깨움
그러면 부모는 sem_wait()에서 리턴하여 프로그램을 종료시킨다.
2. 부모 프로세스가 sem_wait()를 호출하기 전에 자식 프로세스의 실행이 종료된 경우
이 경우 자식이 먼저 sem_post()를 호출하여 세마포어 값을 0에서 1로 증가시킨다.
부모가 실행할 수 있는 상황이 되면 sem_wait()를 호출한다.
부모는 세마포어 값이 1인 것을 발견하고 세마포어 값을 -으로 감소시키고 sem_wait()에서 대기 없이 리턴한다.
계수 세마포어
초기값을 1보다 큰 정수로 설정하여 동일한 종류의 여러 자원을 관리
초기값은 관리할 수 있는 총 자원 수를 정의
자원을 사용할 때마다 세마포어 값 감소, 반환할 때마다 증가
Ex)
5대의 프린트 관리, 세마포어 초기값 5
**
계수 세마포어를 사용하는 이유
1. 하나의 세마포어로 동일한 유형의 자원을 관리할 수 있음
2. 부하를 분산 및 리소스 사용 최적화 (여러 큐로 관리하면 하나의 큐에 작업이 몰릴 수 있음)
3. 구현 단순화
4. 확장성 좋음 // 초기 세마포어 값만 수정하면 됨
5. 프로세스간 동기화 같은 추가적인 기능 구현이 쉬움
같은 자원에 동시에 접근하는 것을 막을 수 있기 때문
6. 데드락 방지
설계가 잘 된 경우 모든 프로세스가 동일한 순서로 자원을 요청하고 해제할 수 있음
**
계수 세마포어는 서로 다른 자원을 관리하는데 사용할 수 있음
단, 복잡성이 증가시킬 수 있어 일반적인 사용 사례는 아님
'컴퓨터 사이언스' 카테고리의 다른 글
교착상태 (0) | 2024.10.26 |
---|---|
경쟁상태(Race Condition) (0) | 2024.10.25 |
임계영역, 공유자원, Lock, Mutex (0) | 2024.10.23 |
프로세스와 스레드 (0) | 2024.10.22 |
CDN (1) | 2024.10.20 |