세마포어(Semaphore)
비동기적으로 실행되는 여러 스레드나 프로세스 간의 **동기화(synchronization)**를 위해 사용하는 중요한 도구이다.
세마포어의 두 가지 연산
- Down 또는 P 연산: 세마포어 값이 양수일 때까지 대기하고, 양수가 되면 그 값을 1 감소시킵니다.
- Up 또는 V 연산: 세마포어 값을 1 증가시키고, 대기 중인 스레드가 있으면 하나를 깨워서 실행시킵니다.
이제 세마포어가 "0,1, 그 이상의 수" 초기화 되는 경우 3가지를 설명합니다.
세마포어의 활용 예시
- 세마포어는 `0`으로 초기화되면 한 번만 발생하는 `이벤트를 기다리기 위한 목적`으로 사용할 수 있다.
- 스레드 A가 스레드 B를 생성하고, 스레드 B가 어떤 작업을 완료할 때까지 A가 기다려야 한다고 가정해봅시다. 스레드 A는 0으로 초기화된 세마포어를 사용하여 B가 작업을 끝냈을 때 신호를 보내도록 할 수 있습니다.
struct semaphore sema;
/* 스레드 A */
void threadA (void) {
sema_down(&sema); // 세마포어 값을 감소시키며, 0이 될 때까지 대기
}
/* 스레드 B */
void threadB (void) {
sema_up(&sema); // 세마포어 값을 증가시키며, 스레드 A를 깨운다
}
/* 메인 함수 */
void main (void) {
sema_init(&sema, 0); // 세마포어 초기값을 0으로 설정
thread_create("threadA", PRI_MIN, threadA, NULL); // 스레드 A 생성
thread_create("threadB", PRI_MIN, threadB, NULL); // 스레드 B 생성
}
스레드 A는 sema_down()을 호출한 후 스레드 B가 sema_up()을 호출할 때까지 멈춥니다. 즉, 스레드 A는 스레드 B가 작업을 완료할 때까지 대기
리소스 접근 제어
세마포어는 1로 초기화하면, 주로 리소스에 대한 접근을 제어하는 데 사용됩니다. 예를 들어, 코드 블록이 어떤 리소스를 사용하기 전에 sema_down()으로 세마포어 값을 감소시켜 접근 권한을 얻고, 작업이 끝나면 sema_up()으로 값을 다시 증가시켜 다른 스레드가 리소스를 사용할 수 있게 합니다.
이 경우에는 **락(Lock)**을 사용하는 것이 더 적합할 수 있습니다. 왜냐하면 락은 특정 리소스에 대한 **상호 배제(Mutual Exclusion)**를 보장해 주기 때문입니다.
세마포어의 초기화
세마포어는 1 이상의 값으로도 초기화할 수 있지만, 그런 경우는 흔하지 않습니다. 세마포어는 **에츠허르 디이크스트라(Edsger Dijkstra)**가 발명했으며, 최초로 THE 운영체제에 사용되었습니다. Pintos에서 세마포어는 include/threads/synch.h에 정의되어 있습니다.
주요 함수 설명
- struct semaphore: 세마포어를 표현하는 구조체입니다.
- void sema_init(struct semaphore *sema, unsigned value): 주어진 초기 값으로 새로운 세마포어를 초기화합니다.
- void sema_down(struct semaphore *sema): P 연산을 수행하여, 세마포어 값이 양수가 될 때까지 대기하고, 양수가 되면 값을 1 감소시킵니다. 이 과정에서 세마포어 값이 0이면 스레드는 대기 상태에 놓이게 됩니다.
- bool sema_try_down(struct semaphore *sema): P 연산을 시도하지만, 기다리지 않고 세마포어 값이 0이면 false를 반환하고, 값이 양수라면 값을 감소시키고 true를 반환합니다. CPU 시간을 낭비할 수 있기 때문에, sema_down()을 사용하는 것이 더 효율적입니다.
- void sema_up(struct semaphore *sema): V 연산을 수행하여 세마포어 값을 1 증가시키고, 대기 중인 스레드가 있으면 그 중 하나를 깨웁니다. 이 함수는 다른 동기화 도구들과 달리, 외부 인터럽트 핸들러 내부에서도 호출될 수 있습니다.
내부 동작
세마포어는 내부적으로 인터럽트 비활성화와 스레드 차단(thread blocking) 및 차단 해제(thread unblocking)를 통해 구현됩니다. 각각의 세마포어는 대기 중인 스레드들의 목록을 유지하는데, 이 목록은 lib/kernel/list.c의 연결 리스트를 사용합니다.
세마포어는 스레드 간의 동기화를 간편하게 해주지만, 복잡한 동기화 시나리오에서는 락(lock)이나 조건 변수(condition variable)와 같은 다른 동기화 도구가 더 적합할 수 있습니다.
'SW 사관학교 정글(Jungle) > 운영체제-PintOS' 카테고리의 다른 글
cpu와 스레드 (0) | 2024.10.01 |
---|---|
[PintOS Project 1 - Threads] 1번 Alarm Clock (0) | 2024.09.28 |
[Pintos : 동기화] 모니터(Monitors) (0) | 2024.09.25 |
[Pintos : 동기화] 락(Lock) (0) | 2024.09.25 |
[Pintos] 동기화(Synchronization) (1) | 2024.09.25 |