이화여자대학교 반효경 교수님의 운영체제 강의 13강을 듣고 정리한 내용이다.
✏️ Bounded-Buffer-Problem(Producer-Consumer-Problem)
- 공유 데이터 - buffer 자체 및 buffer 조작 변수(empty/full buffer의 시작 위치)
- Producer(생산자) - 생산자 프로세스는 공유 버퍼에 데이터를 하나 만들어서 집어넣는 역할
- Empty 버퍼가 있는지 확인하고, 없으면 기다린다.
- 공유 데이터에 lock을 건다.
- Empty 버퍼에 데이터를 입력하고 버퍼를 조작한다.
- lock을 푼다.
- Full 버퍼가 하나 증가한다.
- Consumer(소비자)
- Full 버퍼가 있는지 확인하고, 없으면 기다린다,
- 공유 데이터에 lock을 건다.
- Full 버퍼에서 데이터를 꺼내고 버퍼를 조작한다.
- lock을 푼다.
- Empty 버퍼가 하나 증가한다.
- 발생 가능한 동기화 문제 - 공유 버퍼이기 때문에 생산자 여러 명이 동시에 한 버퍼에 데이터를 쓰거나 수정할 수 있으며, 마찬가지로 생산자가 동시에 한 버퍼의 데이터를 읽어갈 수 있음
- 동기화 변수
- 상호 배제 - 이진 세마포어(공유 데이터의 상호배제를 위해)
- 자원 개수 카운트 - 계수 세마포어(남은 Full/empty 버퍼의 수 표시)
- Mutex - lock을 걸고 푸는 용도로, 공유 버퍼에 단 하나의 프로세스가 접근할 수 있게 1
- Full - 데이터가 들어있는 버퍼를 나타내기 위한 용도로, 처음에는 비어있으니 0
- Empty - 데이터가 비어있는 버퍼를 나타내기 위한 용도
- Producer
- P 연산을 통해 empty 버퍼가 있는지 확인하고, 없으면 대기
- 빈 버퍼가 있다면 mutex를 0으로 만들고 공유 데이터에 lock을 걸은 다음 자원 획득
- 버퍼에 데이터를 입력함
- V 연산을 통해 mutex 값을 1로 만들고, lock을 풀어줌
- V 연산을 통해 full 버퍼를 1 증가하고 임계 구역에서 나옴
- Consumer
- P 연산을 통해 Full 버퍼가 있는지 확인하고, 없으면 대기
- Full 버퍼가 있다면 mutex를 0으로 만들고 임계 구역에 진입
- 버퍼에 데이터를 뺴감
- V 연산을 통해 mutex 값을 1로 만듦
- V 연산을 통해 empty 버퍼를 1 증가하고 임계 구역에서 나옴
✏️ Readers-Writers Problem
- 한 프로세스가 DB에 write 중일 때 다른 프로세스가 접근하면 안됨
- read는 동시에 여럿이 해도 됨
- solution
- writer가 db에 접근 허가를 아직 얻지 못한 상태에서는 모든 대기중인 reader들을 다 db에 접근하게 해준다
- writer는 대기 중인 reader가 하나도 없을 때 db 접근이 허용된다
- 일단 writer가 db에 접근 중이면 reader들은 접근이 금지된다
- writer가 db에서 빠져나가야만 reader의 접근이 허용된다
- 공유 데이터 - DB 자체, 현재 DB에 접근 중인 reader의 수를 나타내는 readcount
int readcount = 0;
DB 자체;
- 동기화 변수
semaphore mutex = 1, db = 1;
- mutex - 공유 변수 readcount를 접근하는 코드의 상호 배제 보장을 위해 사용
- db - reader와 writer가 공유 DB 자체를 올바르게 접근하게 하는 역할
- Writer - starvation 발생 가능
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);
✏️ Dining-Philosophers Problem
- 동기화 변수
semaphore chopstick[5]; /* Initially all values are 1 */
- 철학자 $i$
do {
P(chopstick[i]);
P(chopstick[(i+1) % 5]);
...
eat();
...
V(chopstick[i]);
V(chopstick[(i+1) % 5)];
...
think();
...
} while(1);
- 위 해결방안의 문제점
- Deadlock 가능성이 있다
- 모든 철학자가 동시에 배가 고파져 왼쪽 젓가락을 집어버린 경우
- 해결 방안
- 4명의 철학자만이 동시에 테이블에 앉을 수 있도록 한다
- 젓가락을 두 개 모두 집을 수 있을 때만 젓가락을 집을 수 있게 한다
- 비대칭(짝수/홀수 철학자는 왼쪽/오른쪽 젓가락부터 집도록)
/* 해결 방안 중 "젓가락을 두 개 모두 집을 수 있을 때만 젓가락을 집을 수 있게 한다" 코드 예시 */
enum {thinking, hungry, eating} state[5];
semaphore self[5] = 0;
semaphore mutex = 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]);
}
}
/* Philosopher i */
do {
pickup(i);
eat();
putdown(i);
think();
} while(1);
✏️ Monitor
- Semaphore의 문제점
- 코딩하기 힘들다
- 정확성(correctness)의 입증이 어렵다
- 자발적 협력(voluntary cooperation)이 필요하다
- 한 번의 실수가 모든 시스템에 치명적인 영향을 끼침
- 상호 배제 깨짐
V(mutex);
/* critical section */
P(mutex);
- Deadlock
P(mutex);
/* critical section */
P(mutex);
- 동시 수행중인 프로세스 사이에서 abstract data type의 안전한 공유를 보장하기 위한 high-level synchronization
monitor monitor-name {
shared variable declarations
procedure body P1(...) {
...
}
procedure body P2(...) {
...
}
procedure body Pn(...) {
...
}
{
initialization code
}
}
- 모니터 내에서는 한번에 하나의 프로세스만이 활동 가능
- 프로그래머가 동기화 제약 조건을 명시적으로 코딩할 필요 없음
- 프로세스가 모니터 안에서 기다릴 수 있도록 하기 위해 조건 변수(condition variable) 사용
condition x, y;
- 조건 변수는 wait와 signal 연산에 의해서만 접근 가능 - x.wait()를 invoke한 프로세스는 다른 프로세스가 x.signal()을 invoke하기 전까지 suspend 된다
x.wait();
- x.signal()은 정확하게 하나의 suspend된 프로세스를 resume한다. suspend된 프로세스가 없으면 아무 일도 일어나지 않는다.
x.signal();
- 모니터에서는 lock을 걸거나 풀 필요가 없음
'운영체제' 카테고리의 다른 글
14. Deadlocks 1 (0) | 2022.03.05 |
---|---|
13. Process Synchronization 4 (0) | 2022.03.05 |
11. Process Synchronization 2 (0) | 2022.02.24 |
10. CPU Scheduling 2 & Process Synchronization 1 (0) | 2022.02.19 |
9. CPU Scheduling 1 (0) | 2022.02.19 |