Process들의 실행 순서 맞추기
sync라는 세마포어 변수를 0으로 초기화(물건이 없는 상태)
물건은 Pj가 들고 있었다고 가정.
Pi가 먼저 도착해서 기다리고 있었을 경우, 물건이 없으므로 Li 에서 물건 기다림(Queue)
Pj가 Lj 지나가면서 Pi 꺠워주고 감
만약 Pi가 더 늦게 도착했다면, Pj는 지나가면서 물건만 반납하고(sync=1) 가면 됨. 이 경우엔 깨워주지 않고 지나감(깨울 사람이 없으니까 당연)
Procuder
1. 메세지 생성
2. 소비 되었니? 확인(버퍼가 비었는지 확인) P연산(검사연산). P(consumed)
3. 비었으면 생성한 메시지 버퍼에 두기
4. 생산량 1 증가시킴. V연산(증가). V(produced). Consumer queue에 자는 애 있으면 꺠워줌.
var nrfull: 채워진 buffer 수
var nrempty: 비어있는 buffer 수
mutexP: producer간 상호 배제.(한 번에 한 producer만 일할 수 있음.)
mutexC: consumer간 상호 배제(한 번에 한 consumer만 일할 수 있음.)
Producer Pi
1. create a new message M : 메세지 생성
2. P(mutexP); 현재 일하고 있는 producer가 있는지 확인.
3. P(nrempty): 빈 버퍼가 있는지 확인. 있다면 들어가고 없다면 queue에서 대기
4. buffer[in] <- M 해당하는 버퍼에 물건(메세지)을 놓음
5. in <- (in +1) mod N; 다음 버퍼 위치를 갱신시켜줌(circular queue를 쓰기 때문에 다음 위치를 갱신해줘야 함)
6. V(nrfull) 물건 수 하나 늘렸다고 표시해줌
7. V(mutexP) 상호배제 표시 해제하고 나옴.
Writer Wj는 엄청 간단하다.
- writer mutex 걸고
- write작업 완료 후
- write mutex 해제하고 나온다.
Reader Ri
1. P(rmutex) : 읽기 mutex를 걸어준다
read행위 자체는 동시에 여러 reader가 할 수 있지만,
reader 수 +1하거나, wmutex 거는 작업은 동시 진행이 불가능하기 때문에 Rmutex를 걸어준다.
2. nreaders >0 이면 wmutex가 이미 걸려있을 테니 if문 바로 빠져나오고,
nreaders = 0 이라면 내가 일빠(?)인 셈이니 wmutex를 걸어준다.
3. reader숫자 +1
4. V(rmutex) rmutex를 해제한다.
5. read작업 수행!
6. P(rmutex): 다 읽은 후 reader -1 해주고, 내가 마지막 reader라면 wmutex도 해제하고 나온다.
이를 동시에 여러 reader가 하면 안되니 rmutex를 걸어준다.
semaphore가 busy waiting의 문제는 해결 했지만, queue에 있는 애들 중 누구를 먼저 꺠울지 정해지지 않았기에
starvation 문제가 발생할 수 있었다. 이번에는 이 문제를 해결해보자.
eventcount/sequencer: 은행의 번호표와 비슷한 개념
- 번호표 띵동 하는 기계가 이벤트 카운트라고 보면 된다.
111이라 써있으면 111명의 손님을 처리했고 111번째 손님 오라는 것처럼 eventcount도 같은 기능을 한다.
- advance: 은행원이 버튼을 눌러서 번호를 1 증가시키는 행위
- await(E,v): E는 현재 번호, v는 내 번호. 내 번호가 E보다 크면 E=v 될 때 까지 대기실에서 대기하고, scheduler에게 나 꺠워달라고 부탁함.
1. v <- ticket(S); 프로세스가 도착해서 번호표를 발급 받는다. 첫 손님이니 v는 0번이다.
2. await(E, v); E도 0이고 내 번호표(v)도 0번이니 바로 CS로 들어가서 할 일을 한다.
3. 이때 두 번째 프로세스가 도착해서 ticket(S)을 한다. 두 번째 손님이니 v는 1이다.
4. await(E,v)에서 E는 0(현재 진행중인 손님번호)이고 v는 1이므로 대기실에 가서 쉰다.
5. 첫 손님이 볼 일 다 보고 빠져나가면 E가 0에서 1이 되면서 두번쨰 손님이 CS로 들어간다.
티켓을 뽑고 기다리는게 semaphore의 P연산,
advance는 semaphore의 V연산과 같다.
- var Pticket: producer sequencer
- var Cticket: consumer sequencer
- In: 물건을 놓는 event
- Out: 물건을 꺼내는 event
- Buffer: 크기가 N인 버퍼(물건 놓을 공간)
Producer Pi
t <- ticket(Pticket);
await(In, t);
위 두 연산을 합쳐서 P()연산으로 볼 수 있고,
advance(In); 연산을 V()연산으로 볼 수 있다.
await(Out t-N+1)과 buffer[t mod N] <- M이 critical section이다.
1. Create a new message M : 메세지를 생산함
2. t <- ticket(Pticket); : 티켓을 뽑는다.
3. await(In, t); : 내 번호가 불릴 때까지 기다리고 불리면 들어간다.
4. await(Out, t-N+1) : 공간이 있는지 확인하는 과정.(요게 좀 어렵고 복잡합니다. 아래 설명을 잘 보세요)
- 공간이 있다는 건 '공간 >= 1' 이라는 의미이죠.
- 공간은 기본 N개에, t개만큼 뺴줘야 하고(물건을 놓은 수), 다시 Out만큼 더해줘야 합니다.
- 즉 N - t + Out >= 1 입니다.
- await은 Eventcounter가 좌변에 오게 되있고, < 수식으로 표현하므로 이에 맞게 바꿔줍니다..
* await의 정의: if (E < v)
- Out >= t - N + 1
최종결과: Out < t - N + 1
5. buffer[t mod N] <- M : 버퍼에 물건을 놓는다.
Counsumer Cj
u <- ticket(Cticket);, await(Out, u); 두개를 합쳐서 P()연산을 볼 수 있고,
advance(Out)을 V()연산으로 볼 수 있다.
1. u <- ticket(Cticket); : ticket 뽑는다.
2. await(Out, u); : 내 순서 될 때까지 기다린다.
3. await(In, u + 1); : 물건이 있을 때까지 기다린다.
- 물건이 있다는 건 물건 >= 1
- 물건은 In(물건 놓은 수) - u로 정의할 수 있다.
- In -u >= 1
- 이를 await에 맞게 정리하면?
- In < u + 1
4. m <- buffer[u mod N] : 물건을 꺼내온다.
Monitor
Path expressions
Serializers
Ctirical region, conditional critical region
위 네가지 중 우리는 Monitor만 보기로 한다.
Language-level constructs. 언어 단에서 상호배제를 지원한다.
Object-Oriented concept와 유사하다.
사용이 쉽다.
procedure fillBuf
1. if (validBuf = N) then bufHasSpace.wait();
-> 공간이 있는지 확인하고 없으면 대기. (validBuf는 물건수. 물건수가 N이면 공간이 없다는 뜻)
2. buffer[in] <- data
-> 공간이 생기면 들어가서 물건을 놓는다.
3. validBufs <- validBufs + 1;
-> 물건 수를 하나 증가시켜준다.
4. in <- (in + 1) Mod N;
-> 다음에 물건 놓을 공간을 갱신시켜준다(버퍼가 N개일 때 하는 연산)
5. bufHasData.signal();
-> 버퍼에 데이터가 있다고 알려주는 연산 시행(물건 오길 기다리는 consumer를 깨워줌)
-심심하면 위 문제를 semaphore로 풀어보세요^^ by 교수님
Semaphore가 음이아닌 정수라고 하셨는데 근거가 무엇일까요???
Semaphore는 음의 정수도 가능하지 않나요???