Final
기말 정리 노트
- Haram Lee
- 2026-04-27
- studies / 26-1 / operating-systems
Mutex
pthread_mutex_lock(&m);// shared data accesspthread_mutex_unlock(&m);Bad spinlock
while (l->held) ;l->held = 1;틀린 이유:
check and set not atomicSemaphore bounded buffer
Semaphore mutex = 1;Semaphore empty = N;Semaphore full = 0;produce:wait(empty)wait(mutex)insertsignal(mutex)signal(full)consume:wait(full)wait(mutex)removesignal(mutex)signal(empty)Condition variable wait
pthread_mutex_lock(&m);while (!condition) pthread_cond_wait(&cv, &m);// condition true, do workpthread_mutex_unlock(&m);Condition variable signal
pthread_mutex_lock(&m);condition = true;pthread_cond_signal(&cv);pthread_mutex_unlock(&m);Atomicity bug fix
pthread_mutex_lock(&m);if (ptr != NULL) use(ptr);pthread_mutex_unlock(&m);Ordering bug fix
pthread_mutex_lock(&m);while (ready == 0) pthread_cond_wait(&cv, &m);pthread_mutex_unlock(&m);1. 제일 먼저 외울 큰 분류
pthread_create / pthread_join
→ thread 만들고 기다리는 함수
pthread_mutex_lock / unlock
→ critical section 보호
sem_wait / sem_post 또는 wait / signal
→ resource 개수, event 순서 제어
pthread_cond_wait / signal / broadcast
→ 어떤 조건이 만족될 때까지 잠들기2. Thread 함수들
강의 자료의 Pthreads API는 thread creation/termination 함수로 pthread_create, pthread_exit, pthread_join을 정리하고 있어.
2.1 pthread_create
형태:
int pthread_create(
pthread_t *tid,
pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg
);시험에서는 보통 이렇게 씀:
pthread_t tid;
pthread_create(&tid, NULL, worker, arg);인자 순서:
1. &tid
생성된 thread ID를 저장할 곳
2. NULL
thread attribute. 보통 기본값이면 NULL
3. worker
thread가 실행할 함수 이름
4. arg
worker 함수에 넘길 인자thread 함수는 반드시 이런 모양이어야 함:
void *worker(void *arg) {
// thread code
return NULL;
}예시:
void *hello(void *arg) {
printf("hello\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, hello, NULL);
pthread_join(tid, NULL);
return 0;
}헷갈리면 이렇게 외워:
pthread_create(&tid, NULL, 함수이름, 함수인자)2.2 pthread_join
형태:
int pthread_join(pthread_t tid, void **thread_return);자주 쓰는 형태:
pthread_join(tid, NULL);인자:
1. tid
기다릴 thread ID
여기서는 &tid가 아니라 tid
2. NULL
thread의 return value를 안 받을 거면 NULL주의:
pthread_create(&tid, NULL, worker, NULL);
// ↑ &tid
pthread_join(tid, NULL);
// ↑ tid
create는 tid를 “채워줘야” 하니까 &tid.join은 이미 있는 tid를 “지정”하는 거라 tid.
3. Mutex 함수들
강의 자료에서 mutex 함수는 pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_unlock으로 정리되어 있어.
3.1 기본 형태
pthread_mutex_t m;초기화:
pthread_mutex_init(&m, NULL);사용:
pthread_mutex_lock(&m);
// critical section
pthread_mutex_unlock(&m);삭제:
pthread_mutex_destroy(&m);3.2 인자 정리
pthread_mutex_init(&m, NULL);1. &m
초기화할 mutex의 주소
2. NULL
attribute. 기본값이면 NULLpthread_mutex_lock(&m);
pthread_mutex_unlock(&m);인자: &m
→ lock/unlock할 mutex의 주소3.3 언제 쓰나?
공유 변수를 건드릴 때.
int g = 0;
pthread_mutex_t m;
void *worker(void *arg) {
pthread_mutex_lock(&m);
g++;
pthread_mutex_unlock(&m);
return NULL;
}g++는 shared global variable을 write하므로 critical section이다. 강의 자료에서도 global variable은 thread들이 공유하고, g++가 여러 instruction으로 쪼개져 race가 생길 수 있다고 설명한다.
4. Semaphore 함수들
강의에서는 semaphore를 wait()와 signal()로 설명한다. POSIX에서는 보통 이름이 sem_wait()와 sem_post()다. 개념은 같아. Semaphore는 integer value를 가지며, wait는 감소시키고 필요하면 block, signal은 증가시키고 waiting thread 하나를 깨운다.
4.1 강의식 pseudo-code
wait(&S);
signal(&S);4.2 POSIX식 이름
sem_wait(&S);
sem_post(&S);시험/강의에서 wait, signal로 쓰면 그걸 따르면 됨.
4.3 의미
wait(&S)
→ S 값을 하나 감소
→ 값이 부족하면 기다림
signal(&S)
→ S 값을 하나 증가
→ 기다리는 thread가 있으면 하나 깨움4.4 Binary semaphore
초기값 1.
Semaphore mutex = 1;
wait(&mutex);
// critical section
signal(&mutex);의미:
한 번에 하나만 들어가게 함
mutex처럼 사용 가능4.5 Counting semaphore
초기값 N.
Semaphore empty = N;의미:
N개의 resource가 있음
동시에 최대 N개 thread가 통과 가능5. Bounded buffer semaphore 패턴
이건 진짜 외워야 해.
초기값:
Semaphore mutex = 1;
Semaphore empty = N;
Semaphore full = 0;5.1 Producer
void produce(item x) {
wait(&empty);
wait(&mutex);
buffer[in] = x;
in = (in + 1) % N;
signal(&mutex);
signal(&full);
}5.2 Consumer
item consume() {
item x;
wait(&full);
wait(&mutex);
x = buffer[out];
out = (out + 1) % N;
signal(&mutex);
signal(&empty);
return x;
}강의 자료의 bounded buffer 정답도 producer는 wait(empty) → wait(mutex) → insert → signal(mutex) → signal(full), consumer는 wait(full) → wait(mutex) → remove → signal(mutex) → signal(empty) 순서야.
5.3 왜 이 순서인가?
Producer:
wait(empty)
→ 빈 칸이 있어야 넣을 수 있음
wait(mutex)
→ buffer, in을 혼자 수정
signal(mutex)
→ buffer 수정 끝
signal(full)
→ item 하나 생겼다고 알림Consumer:
wait(full)
→ item이 있어야 꺼낼 수 있음
wait(mutex)
→ buffer, out을 혼자 수정
signal(mutex)
→ buffer 수정 끝
signal(empty)
→ 빈 칸 하나 생겼다고 알림5.4 왜 wait(&mutex)를 먼저 하면 안 좋나?
잘못된 producer:
wait(&mutex);
wait(&empty);만약 buffer가 full이면 producer는:
mutex를 잡은 채 empty를 기다림consumer가 item을 꺼내야 empty가 생기는데, consumer는 mutex를 못 잡음.
producer는 empty 기다림
consumer는 mutex 기다림
→ deadlock 가능그래서 resource semaphore 먼저, 그다음 mutex.
외우기:
Producer: empty 먼저, mutex 나중
Consumer: full 먼저, mutex 나중6. Condition Variable 함수들
강의 자료에서 condition variable 함수는 pthread_cond_wait, pthread_cond_signal, pthread_cond_broadcast로 정리되어 있고, wait(cv, mutex)는 mutex가 잡힌 상태에서 호출되며 caller를 sleep시키고 mutex를 atomic하게 release한다고 설명한다.
6.1 기본 함수
pthread_cond_t cv;
pthread_mutex_t m;초기화:
pthread_cond_init(&cv, NULL);
pthread_mutex_init(&m, NULL);wait:
pthread_cond_wait(&cv, &m);signal:
pthread_cond_signal(&cv);broadcast:
pthread_cond_broadcast(&cv);destroy:
pthread_cond_destroy(&cv);6.2 인자 순서
pthread_cond_wait(&cv, &m);1. &cv
기다릴 condition variable
2. &m
현재 잡고 있는 mutex헷갈리면:
cond_wait(조건큐, 그 조건을 보호하는 락)즉:
pthread_cond_wait(¬_empty, &m);의미:
not_empty 조건을 기다리는데,
그 조건과 관련된 shared state는 m으로 보호한다.6.3 pthread_cond_signal
pthread_cond_signal(¬_empty);인자:
깨울 condition variable 하나6.4 pthread_cond_broadcast
pthread_cond_broadcast(¬_empty);인자:
깨울 condition variable 하나차이:
signal
→ 기다리는 thread 하나 깨움
broadcast
→ 기다리는 thread 전부 깨움7. Condition Variable 코드 패턴
7.1 Consumer
item consume() {
item x;
pthread_mutex_lock(&m);
while (count == 0)
pthread_cond_wait(¬_empty, &m);
x = buffer[out];
out = (out + 1) % N;
count--;
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&m);
return x;
}7.2 Producer
void produce(item x) {
pthread_mutex_lock(&m);
while (count == N)
pthread_cond_wait(¬_full, &m);
buffer[in] = x;
in = (in + 1) % N;
count++;
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&m);
}7.3 구조만 외우면 됨
lock
while (bad condition)
cond_wait(condition_variable, mutex)
do work
signal(other condition variable)
unlockProducer에서 bad condition:
count == N
→ buffer full
→ 더 넣으면 안 됨
→ not_full 기다림Consumer에서 bad condition:
count == 0
→ buffer empty
→ 꺼낼 게 없음
→ not_empty 기다림8. while(변수) / while(조건) 읽는 법
C에서 while (...) 안에는 조건식이 들어간다.
while (조건) {
반복할 코드
}의미:
조건이 참이면 계속 반복
조건이 거짓이면 빠져나옴C에서는:
0 → false
0이 아님 → true그래서 이런 것도 가능함.
while (x) {
...
}이건:
while (x != 0) {
...
}와 같은 뜻.
9. while (l->held); 이건 뭐야?
while (l->held)
;또는 한 줄로:
while (l->held);여기서 ;는 empty statement야.
즉:
l->held가 1인 동안 아무것도 안 하고 계속 반복뜻:
lock이 잡혀 있으면 기다려라예:
void acquire(struct lock *l) {
while (l->held)
;
l->held = 1;
}읽는 법:
while lock is held, spin.
when lock is not held, set held to 1.문제는 이 구현은 multicore에서 틀림.
왜냐하면 while (l->held)로 검사하는 것과 l->held = 1로 잡는 것이 atomic하지 않기 때문. 강의 자료에서도 이 구현을 “initial attempt”로 보여주고, multicore에서는 atomic instruction이 필요하다고 이어진다.
10. while (count == 0)은 왜 쓰는 거야?
Consumer 입장에서:
while (count == 0)
pthread_cond_wait(¬_empty, &m);count == 0의 뜻:
buffer가 비어 있음
꺼낼 item이 없음그러면 consumer는 지금 진행하면 안 됨.
그래서:
while buffer is empty, wait.즉, 좋은 조건을 기다리는 게 아니라, 나쁜 조건인 동안 기다리는 것이야.
count == 0
→ bad condition
→ wait
count > 0
→ good condition
→ consumeProducer는 반대:
while (count == N)
pthread_cond_wait(¬_full, &m);count == N의 뜻:
buffer가 꽉 참
더 넣을 수 없음그래서:
while buffer is full, wait.11. 왜 if가 아니라 while?
잘못된 코드:
if (count == 0)
pthread_cond_wait(¬_empty, &m);이게 위험한 이유:
깨어났다고 해서 count > 0이라는 보장이 없음가능한 상황:
1. Consumer A가 count==0이라 wait
2. Producer가 item 넣고 signal
3. Consumer A가 깨어남
4. 그런데 Consumer B가 먼저 실행돼서 item을 가져감
5. Consumer A가 다시 실행될 때 count==0일 수 있음그래서 깨어난 뒤에도 다시 검사해야 해.
정답:
while (count == 0)
pthread_cond_wait(¬_empty, &m);시험 문장:
Condition variables should be used with a while loop because waking up does not guarantee that the condition is true.
또 CV는 signal history가 없어서, shared state인 count, ready 같은 변수를 기준으로 판단해야 한다. 강의 자료에서도 CV signal은 기다리는 thread가 없으면 사라지고, semaphore와 달리 history가 없다고 설명한다.
12. pthread_cond_wait(&cv, &m) 내부에서 무슨 일이 일어나나?
이 한 줄이 제일 중요해.
pthread_cond_wait(&cv, &m);이 함수는 다음을 함:
1. 현재 thread를 cv의 waiting queue에 넣음
2. mutex m을 release함
3. thread를 sleep시킴
4. 나중에 깨어나면 mutex m을 다시 acquire함
5. return핵심:
release mutex + sleep이 두 동작이 atomic해야 함.
왜냐면 atomic하지 않으면:
Thread A: count == 0 확인
Thread A: wait하려고 함
Thread A: mutex release
Thread B: item 넣고 signal
Thread A: 이제 sleep이러면 A는 signal을 놓치고 잠들 수 있음. 이게 lost wakeup.
13. Ordering bug에서 while 패턴
예:
int ready = 0;
pthread_mutex_t m;
pthread_cond_t cv;Thread 2가 object가 준비될 때까지 기다리는 코드:
pthread_mutex_lock(&m);
while (ready == 0)
pthread_cond_wait(&cv, &m);
pthread_mutex_unlock(&m);
use_object();Thread 1이 준비시키는 코드:
init_object();
pthread_mutex_lock(&m);
ready = 1;
pthread_cond_signal(&cv);
pthread_mutex_unlock(&m);읽는 법:
while ready == 0
→ 아직 준비 안 됨
→ 기다려라
ready == 1
→ 준비됨
→ 진행해라강의 자료의 ordering bug 해결 예시도 while (mtInit == 0) cond_wait(...) 형태야.
14. 헷갈리는 함수 인자 모음표
Thread
pthread_create(&tid, NULL, func, arg);&tid : thread ID 저장할 곳
NULL : attr
func : thread가 실행할 함수
arg : func에 넘길 인자pthread_join(tid, NULL);tid : 기다릴 thread
NULL : return value 안 받음Mutex
pthread_mutex_init(&m, NULL);
pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);
pthread_mutex_destroy(&m);전부 핵심은:
mutex는 &m 넘김Condition variable
pthread_cond_init(&cv, NULL);
pthread_cond_wait(&cv, &m);
pthread_cond_signal(&cv);
pthread_cond_broadcast(&cv);
pthread_cond_destroy(&cv);핵심:
wait만 인자가 2개
pthread_cond_wait(&cv, &mutex)Semaphore
강의식:
wait(&S);
signal(&S);POSIX식:
sem_wait(&S);
sem_post(&S);핵심:
semaphore는 &S 넘김15. 자주 나오는 코드 빈칸 패턴
15.1 Mutex critical section
pthread_mutex_lock(&m);
shared_data++;
pthread_mutex_unlock(&m);15.2 CV consumer
pthread_mutex_lock(&m);
while (count == 0)
pthread_cond_wait(¬_empty, &m);
remove_item();
count--;
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&m);15.3 CV producer
pthread_mutex_lock(&m);
while (count == N)
pthread_cond_wait(¬_full, &m);
insert_item();
count++;
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&m);15.4 Semaphore producer
wait(&empty);
wait(&mutex);
insert_item();
signal(&mutex);
signal(&full);15.5 Semaphore consumer
wait(&full);
wait(&mutex);
remove_item();
signal(&mutex);
signal(&empty);16. while 조건을 한국어로 번역하는 습관
while (count == 0)count가 0인 동안
즉, buffer가 비어 있는 동안
while (count == N)count가 N인 동안
즉, buffer가 가득 찬 동안
while (ready == 0)ready가 0인 동안
즉, 아직 준비되지 않은 동안
while (l->held)lock이 held인 동안
즉, lock이 잡혀 있는 동안
while (!done)done이 false인 동안
즉, 아직 끝나지 않은 동안
while (ptr)ptr이 NULL이 아닌 동안
17. 왜 while 안 조건이 “기다리는 조건”처럼 보이나?
이게 헷갈리는 진짜 이유야.
while (count == 0)
wait();이건 “count가 0이 되기를 기다려라”가 아니야.
정확히는:
count가 0인 동안 기다려라.
count가 0이 아니게 되면 진행해라.즉 while 안에는 보통:
내가 진행하면 안 되는 조건을 넣는다.
정리:
consumer:
while (buffer empty) wait
producer:
while (buffer full) wait
ordering:
while (not ready) wait
spinlock:
while (lock held) spin18. 마지막 암기 카드
pthread_create(&tid, NULL, func, arg)
→ thread 생성
pthread_join(tid, NULL)
→ thread 끝날 때까지 기다림
pthread_mutex_lock(&m)
→ lock 잡기
pthread_mutex_unlock(&m)
→ lock 풀기
pthread_cond_wait(&cv, &m)
→ cv에서 기다림, m은 자동으로 release 후 reacquire
pthread_cond_signal(&cv)
→ cv에서 기다리는 thread 하나 깨움
wait(&empty)
→ 빈 칸 하나 확보
wait(&full)
→ 찬 칸 하나 확보
wait(&mutex)
→ critical section 입장
signal(&mutex)
→ critical section 퇴장
while (bad condition)
→ 나쁜 조건인 동안 기다려라
CV에서는 if 말고 while
→ 깨어나도 조건이 참이라는 보장이 없기 때문진짜 시험장에서는 이것만 떠올리면 돼:
CV 패턴:
lock
while (bad condition)
cond_wait(cv, lock)
work
signal(other_cv)
unlock
Semaphore bounded buffer:
producer: empty → mutex → insert → mutex → full
consumer: full → mutex → remove → mutex → empty