
2025.05.09
오늘한 내용 : OS - 동기화(Synchronization)
WEEK 09 : 정글 끝까지(PintOS) - Threads
동기화(synchronization)는 다수의 스레드나 프로세스가 동시에 하나의 자원(메모리, 파일, I/O 장치 등)에 접근할 때 발생할 수 있는 경쟁 상태(race condition)와 데이터 손상을 방지하기 위해, 접근 순서와 상호 배제를 보장하는 기법
include/threads/interrupt.h에 선언
| 함수 | 설명 |
|---|---|
enum intr_level intr_disable(void); | 현재 인터럽트 상태를 저장한 뒤, 인터럽트를 꺼버린다. |
enum intr_level intr_enable(void); | 현재 상태를 저장한 뒤, 인터럽트를 켠다. |
enum intr_level intr_set_level(enum intr_level level); | INTR_OFF 또는 INTR_ON으로 상태를 설정하고, 이전 상태를 반환한다. |
enum intr_level intr_get_level(void); | 현재 인터럽트 활성화 여부(INTR_ON/INTR_OFF)를 반환한다. |
counter)와 이를 원자적으로 조작하는 두 개의 연산자(P, V)로 구성내부 값(count)이 곧 “허용된 동시 접근(또는 발생 가능한 이벤트 횟수)”을 나타낸다.
count가 0이면 더 이상 자원 접근(또는 이벤트 기다림)이 불가능하며, 대기 중인 스레드는 블록(block)된다.
| 연산 | 이름 | 동작 |
|---|---|---|
P | down | count > 0이 될 때까지 대기(블록) → count– |
V | up | count++ → 만약 블록된 스레드가 있으면 하나를 깨워(unblock) 다시 준비 상태로 전환 |
P(↓)
count가 0이라면 스레드를 대기 큐(waiters)에 넣고 블록한다.count가 1 이상이 되면 바로 count–를 수행하고, 임계 구역(공유 자원)으로 진입한다.V(↑)
count++를 수행한다.struct semaphore sema;
void threadA(void) {
sema_down(&sema); // B가 up 해줄 때까지 블록
// B 완료 후 수행할 작업…
}
void threadB(void) {
// 작업 수행…
sema_up(&sema); // A를 깨움
}
int main(void) {
sema_init(&sema, 0);
thread_create("A", PRI_DEFAULT, threadA, NULL);
thread_create("B", PRI_DEFAULT, threadB, NULL);
}
| 초기값(value) | 처음 sema_down() 동작 | count 변화 | 차이점 |
|---|---|---|---|
| 0 | sema_down()을 호출한 스레드는 바로 블록된다. (카운터가 0이기 때문) | 변화 없음 (대기) | “B가 sema_up()을 호출해야만 A가 깨어난다.” |
| 1 | sema_down()을 호출한 스레드는 블록 없이 즉시 진입한다. (카운터가 1이므로) | count가 0으로 감소 | “락(lock)처럼, 한 스레드만 진입시키고 다음엔 대기시킨다.” |
| >1 | sema_down()을 호출한 만큼(예: value=2→두 번) 대기 없이 진입한다. | 호출 횟수만큼 count가 감소 | “최대 n개 스레드(자원)까지 동시 접근을 허용한다.” |
include/threads/synch.h에 선언
| 함수 | 설명 |
|---|---|
void sema_init(struct semaphore *s, unsigned value); | 세마포어 객체 s를 value로 초기화 |
void sema_down(struct semaphore *s); | P 연산 수행. 값이 0이면 블록 후 대기, 양수가 되면 즉시 count– |
bool sema_try_down(struct semaphore *s); | 블록 없이 즉시 P 연산 시도, 성공 시 true/실패 시 false 반환 |
void sema_up(struct semaphore *s); | V 연산 수행. count++ 후 대기 스레드 하나를 깨움 |
intr_disable()/intr_enable() )로 원자성(atomicity)을 보장lib/kernel/list.c의 연결리스트를 이용해 스레드 대기열 유지include/threads/synch.h 에 선언
| 함수 | 설명 |
|---|---|
lock_init(struct lock *lock); | 락을 초기화한다. |
lock_acquire(struct lock *lock); | 락을 획득할 때까지 블록. |
bool lock_try_acquire(struct lock *lock); | 즉시 락 획득 시도, 실패 시 false 반환. |
lock_release(struct lock *lock); | 락 소유권을 해제. |
bool lock_held_by_current_thread(const struct lock *lock); | 현재 스레드가 락을 소유했는지 확인. |
lock_acquire() 호출 → 락이 이미 잡혀 있으면 락 대기열에 들어가 블록cond_wait() 호출cond_signal() 또는 cond_broadcast() 호출 → 조건 변수 대기열 중 하나(또는 전부)를 깨움cond_wait() 함수가 복귀하며 다시 락을 획득한 뒤 임계 영역에 진입lock_release() 호출 → 락을 반납thread_unblock() 으로 준비 큐에 올려 스케줄러가 실행하도록 함include/threads/synch.h 선언| 함수 | 설명 |
|---|---|
| 모니터 락 | |
void lock_init(struct lock *lk); | 락 객체를 초기화한다. |
void lock_acquire(struct lock *lk); | 락을 획득할 때까지 블록(block)한다. |
bool lock_try_acquire(struct lock *lk); | 블록 없이 즉시 락 획득을 시도, 성공 시 true, 실패 시 false. |
void lock_release(struct lock *lk); | 락 소유자(owner)가 락을 해제한다. |
| 조건 변수 | |
void cond_init(struct condition *cv); | 조건 변수 객체를 초기화한다. |
void cond_wait(struct condition *cv, struct lock *lk); | 모니터 락을 해제하고 대기, 신호 수신 시 락을 재획득한다. |
void cond_signal(struct condition *cv, struct lock *lk); | 대기 중인 스레드 하나를 깨운다. |
void cond_broadcast(struct condition *cv, struct lock *lk); | 대기 중인 모든 스레드를 깨운다. |
ticks) 대기 루프에서 while (ticks == start) barrier();로 무한 루프 최적화 방지.while (loops-- > 0) barrier();를 사용.include/threads/synch.h에 정의된 barrier() 매크로가 최적화 배리어 역할을 한다.