데이터를 읽음 → 연산 수행 → 결과 내보냄
하나의 공유 데이터에 동시에 접근하려고 할 때 문제
Race Condition (경쟁 상태)
공유 데이터 동시 접근 → 데이터의 불일치 문제
일관성 유지를 위해 협력 프로세스 간의 실행 순서를 정해야 함
Critical Section: 각 프로세스의 코드에서 공유 데이터를 접근하는 코드
한 프로세스가 critical section에 있을 때 다른 프로세스는 접근할 수 없음
Synchronization variable turn
ex) turn == 0 → 상대방 차례 / turn == 1 → 내 차례
do {
while (turn != 0) ; /* My turn? */
critical section
turn = 1; /* Now it's your turn */
remainder section
} while (1);
Mutual Exclusion 만족
Progress 만족 X
반드시 교대로 들어가야 하기 때문에 상대방이 turn을 내 값으로 바꿔줘야만 들어갈 수 있음
Synchronization variable boolean flag[2]
상대방 flag == False 될때까지 기다림
do {
flag[i] = true; /* Pretend I am in */
while (flag[j]) ; /* Is he also in? then wait */
critical section
flag[i] = false; /* I am out now */
remainder section
} while (1);
Mutual Exclusion 만족
Progress 만족 X
둘 다 2행까지 수행 후 끊임없이 양보하는 상황 발생 가능
Synchronization variable turn
, boolean flag[2]
do {
flag[i] = true; /* My intention is to enter */
turn = j; /* Set to his turn */
while (flag[i] && turn == j) ; /* wait only if... */
critical section
flag[i] = false;
remainder section
} while (1);
모든 조건 만족
Busy Waiting (= Spin Lock)
계속 CPU와 memory를 쓰면서 wait (while문 반복)
하드웨어적으로 Test & Set를 atomic하게 수행할 수 있도록 지원하는 경우 문제 간단히 해결
Synchronization variable: boolean lock = false;
do {
while (Test_and_Set(lock)) ;
critical section
lock = false;
remainder section
}
Test_and_set(lock)
lock의 값을 읽음 → lock == 0 → lock = 1로 변경하고 critical section 들어감 → 빠져나올 때 lock = 0으로 변경
추상 자료형 - object, operation으로 구성
Semaphore S - integer variable
공유 자원 counting, 자원이 남아있는지 확인
while (S <= 0) /* 자원이 없음 */
do no-op;
S--; /* 자원 사용 */
S++; /* 자원 반납 */
Synchronization variable: semaphore mutex;
(1로 초기화)
do {
P(mutex); /* If positive, dec-&-enter, Otherwise, wait */
critical section
V(mutex); /* Increment semaphore */
remainder section
} while (1);
busy-wait 문제 → Block & Wakeup 방식
Semaphore를 구조체처럼 정의
typedef struct {
int value; /* semaphore */
struct process *L; /* process wait queue */
} semaphore;
P(S):
S.value--; /* prepare to enter */
if (S.value < 0) { /* Oops, negative, I cannot enter */
add this process to S.L;
block();
}
V(S):
S.value++;
if (S.value <= 0) {
remove a process P from S.L;
wakeup(P);
}
S.value ≤ 0 → 자원의 여분이 없어서 어떤 프로세스가 잠들어 있음
S.value > 0 → 자원이 남아 있음
Producer-Consumer Problem
Producer: data 생성해서 버퍼에 넣음
Consumer: data 꺼내감
semaphore full = 0
empty = n
mutex = 1
Producer
do {
...
produce an item in x
...
P(empty);
P(mutex);
...
add x to buffer
...
V(mutex);
V(full);
...
} while(1);
Consumer
do {
P(full);
P(mutex);
...
remove an item from buffer to y
...
V(mutex);
V(empty);
...
comsume the item in y
...
} while(1);
한 프로세스가 DB에 write 중일 때 다른 프로세스 접근 X
read는 동시에 여럿이 해도 됨
DB 자체
int readcount = 0;
semaphore mutex = 1;
db = 1;
Writer
P(db);
...
writing DB is performed
...
V(db);
Reader
P(mutex);
readcount++;
if (readcount == 1)
P(db) /* block writer */
V(mutex); /* readers follow */
...
reading DB is performed
...
P(mutex);
readcount--;
if (readcount == 0)
V(db); /* enable writer */
V(mutex);
Starvation 발생 가능
Synchronization variables: semaphore chopstick(5);
Deadlock 가능성이 있음 (모든 철학자가 동시에 배가 고파져 왼쪽 젓가락을 집은 경우)
enum {thinking, hungry, eating} state[5];
semaphore self[5] = 0;
젓가락을 모두 집을 수 있는 상태인지
semaphore mutex = 1;
Philosopher
do {
pickup(i);
eat();
putdown(i);
think();
} while(1);
void putdown(int i) {
P(mutex);
state[i] = thinking;
test((i+4) % 5);
test((i+1) % 5);
V(mutex);
}
void pickup(int i) {
P(mutex);
state[i] = hungry;
test(i);
V(mutex);
P(self[i]);
}
void test(int i) {
if (state[(i+4)%5] != eating && state[i] == hungry && state[(i+1)%5] != eating) {
state[i] = eating;
V(self[i]);
}
}
Semaphore의 문제점
코딩이 힘듦, 정확성 입증이 어려움, 자발적 협력이 필요, 한 번의 실수가 모든 시스템에 치명적 영향
Monitor
공유 데이터에 대한 동시접근을 모두 책임짐
모니터 안에 정의되어 있는 함수로만 접근 가능
프로세스가 모니터 안에서 기다릴 수 있도록 하기 위해 condition variable 사용 condition x, y;
x. wait();
- 프로세스 blocked 상태
x.signal();
- blocked 상태의 프로세스 호출
monitor bounded_buffer {
int buffer[N];
condition full, empty;
void produce(int x) {
if there is no empty buffer
empty.wait();
add x to an empty buffer
full.signal();
}
void consume(int *x) {
if there is no full buffer
full.wait();
remove an item from buffer and store it to *x
empty.signal();
}
}
condition variable - 값을 가지지 않고 프로세스를 재우거나 깨우는 역할만 수행monitor dining_philosopher {
enum {thinking, hungry, eating} state[5];
condition self[5];
void pickup(int i) {
state[i] = hungry;
test(i);
if (state[i] != eating
self[i].wait();
}
void putdown(int i) {
state[i] = thinking;
test((i+4) % 5);
test((i+1) % 5);
}
void test(int i) {
if (state[(i+4) % 5] != eating) && (state[i] == hungry) && (state[(i+1) % 5 != eating)) {
state[i] = eating;
self[i].signal();
}
}
void init() {
for (int i = 0; i < 5; i++)
state[i] = thinking;
}
Each Philosopher: {
pickup(i);
eat();
putdown(i);
think();
} while(1);