공부함

Process Synchronization 본문

운영체제/이화여대 강의

Process Synchronization

찌땀 2023. 10. 17. 17:55

https://core.ewha.ac.kr/publicview/C0101020140401134252676046?vmode=f

 

반효경 [운영체제] 11. CPU Scheduling 2 / Process Synchronization 1

설명이 없습니다.

core.ewha.ac.kr

 

데이터의 접근

컴퓨터 시스템 안에서 데이터에 접근 할 떄는 위와 같은 flow를 따르게 된다. 

먼저 연산할 데이터에 접근하고 연산한 후 연산한 결과를 다시 저장하게 된다. 

 

데이터를 읽기만 하면 문제될 게 없다.

데이터를 읽어 와서 연산한 후 결과를 다시 저장하면 누가 먼저 읽었는지에 따라 결과가 달라질 수 있고

이것을 Synchronization 문제라고 한다. 

 

Race Condition 

 

S box에 접근하는 E box가 여러개 있으면?

여러 주체가 데이터에 동시에 접근하려고 하는 것을 Race condition 경쟁상태라고 한다. 

CPU가 하나만 있는 상황에서는 이런 문제가 생기지 않는다

MultiProcessor System에서는 이런 문제가 생길 수 있다

또 공유메모리를 사용하는 프로세스의 경우 문제가 생길 수 있다. 

 

프로세스가 OS에게 대신 요청하는 system call의 경우 커널의 코드가 실행이 된다. 

이것은 커널의 데이터에 접근을 하는 것이다. 

이 때 CPU를 뺏겨서 다른 프로세스도 system call을 해서 커널의 데이터를 접근한다면?

이런 경우에 Race condition이 발생할 수 있다.

 

커널의 코드가 실행 중인데 interrupt가 들어올 수 있다. 

이러면 하던 일을 잠시 멈추고 인터럽트 처리 루틴을 실행하는데 ICR 또한 커널의 코드이므로 race condition이 발생할 수 있다. 

 

커널에 있는 data는 공유데이터, 즉 여러 프로세스가 동시에 사용할 수 있는 data이므로 문제가 생길 수 있다.

 

OS에서의 race condition 

상황 1 

Count++라는 고급 언어의 명령어는 CPU상에서 load, Inc, Store 3가지의 과정으로 이루어진다.

만약 커널 함수가 count를 load 한 상태에서 Interrupt가 발생다면?

 ICR도 커널 코드이기 때문에 kernel address space를 공유하게 된다. 

 

이럴 경우에는 먼저 작업중인 일이 끝나기 전에 인터럽트를 받지 않도록 하면 된다 

 

상황 2

System call로 인한 커널 모드에서 CPU 제어권이 넘어가고 넘어간 프로세스에서도 커널 모드에서 Count에 접근 

이것의 해결책으로는 프로세스가 커널 모드에 있을 때는 할당 시간이 끝나도 CPU를 뺏기지 않도록 하는 것이다. 

커널 모드가 끝나고 user mode로 빠져나올 때 CPU를 뺏는다.

이렇게 하면 할당 시간이 정확하게 지켜지진 않지만 time sharing system에서는 크게 상관 없다. 

 

상황3

CPU가 여러개 있는 환경이다. 

작업 주체가 여러개 있어서 생기는 문제점으로 인터럽트를 막거나 해서 해결할 수가 없다. 

 

이때는 data에 접근할 때 lock을 걸어야 한다.

접근하기 전에 lock을 걸어서 내가 데이터를 수정하는 동안 다른 누구도 데이터에 접근하지 못하게 하는 것이다. 

 

또는 커널을 접근하는 CPU를 매 순간 하나만 접근할 수 있게 막는 것이다. 

왜냐하면 결국 커널에 CPU가 동시에 접근해서 발생하는 문제이기 때문이다. 

즉 커널 전체에 하나의 lock을 거는 것이다. 

이 방법을 사용하면 CPU가 여러개인 이점을 살리기 힘들다. 비효율적이다. 

 


정리

Process Synchronization 문제는 공유데이터에 동시접근하려는 상황에서 데이터의 불일치 문제를 발생시키는 것이다.

일관성 유지를 위해 공유데이터를 접근하는 프로세스 간의 실행 순서를 접근하는 메커니즘이 필요하다.

 

Race Condition

여러 프로세스들이 공유데이터에 동시에 접근하는 상황 

데이터의 최종 연산 결과는 데이터를 마지막으로 다룬 프로세스에 따라 달라짐 

 

shared memory를 쓰고있거나, 커널에 있는 데이터를 건드리던 도중에 CPU 제어권이 넘어가면 발생하는 문제

단순히 process P1에서 P2로 넘어간다고 발생하는 문제는 아니다.

프로세스는 자신만의 독자적인 메모리 공간에서 작업하기 때문이다. 

 

Critical Section Problem 

공유데이터에 접근하는 코드인 X=X+1, X=X-1이 Critical Section이다

공유데이터를 접근하는 코드를 Critical Section이라 부른다 

하나의 프로세스가 critical section에 있을 때 다른 모든 프로세스는 critical section에 들어갈 수 없어야 한다. 

 


프로그램적 해결법의 충족 조건 

여러 프로세스가 동시에 Critical Section에 들어가는 걸 막가 위해 들어가기 전에 lock을 건다 

소프트웨어적으로 해결하기 위해서는 3가지 조건을 충족해야 한다  

 

  • Mutual Exclusion 상호 배제 
    • 프로세스가 critical section을 수행중이면 다른 모든 프로세스들은 그들의 critical section에 들어가면 안된다
  • Progress 진행 
    • 아무도 critical section에 있지 않은 상태에서 critical section에 들어가고자 하는 프로세스가 있으면 critical section에 들어가게 해줘야 한다 
  • Bounded Waiting 기다리는 시간이 유한해야 한다 
    • 프로세스가 critical section에 들어가려고 요청한 후 부터 그 요청이 허용될 때 까지 다른 프로세스들이 critical section에 들어가는 횟수에 한계가 있어야 한다 

가정 

- 모든 프로세스들의 수행 속도는 0보다 크다 

- 프로세스들 간의 상대적인 수행 속도는 가정하지 않는다 

 

알고리즘1

 

0번 프로세스의 코드

turn은 어느 프로세스의 차례인지 나타낸다

critical section에 들어가는 것이 누구 차례인지 나타내는 변수다

while (turn!=0); 부분에서 0번 프로세스는 자기 차례가 아니므로 계속 기다린다 

turn이 0으로 바뀌면 (1번 프로세스가 바꿔준다) 프로세스 0의 차례가 된 것이고 critical section으로 들어간다 

나오고 나서 turn=1로, 즉 프로세스 1번의 차례로 바꿔준다. 

 

두 프로세스가 동시에 critical section에 들어가지 않으므로 mutual exclusion은 만족한다.

하지만 이 코드의 문제점은 progress를 만족하지 않는 것이다. 아무드 프로세스에 없어도 못 들어갈수가 있다.

critical section에는 교대로 들어가게 코드가 짜져있다. 

이러면 문제가 프로세스 0은 프로세스 1에 비해 빈번하게 critical section에 들어가고 싶어도 프로세스 1이 일단 critical section에 들어가면 기다려야 한다. 

 

 

알고리즘2

boolean flag[2];
initally flag[모두] = false; // 처음엔 아무도 CS에 안들어감

프로세스 i의 코드

 

flag 변수를 사용한다.

프로세스가 각각 자신의 프로세스를 갖고 있고, 본인이 critical section에 들어가고자 하는 의중을 나타낸다 

flag는 처음에 false로 초기화 된다.

flag[i]가 true면 프로세스 i가 CS에 들어갈 준비가 된 것이다. 

 

먼저 flag[i]=true로 놓는다. 

그리고 flag[j]가 true라면, 즉 프로세스 j가 CS에 들어가 있으면 while(flag[j])로 기다린다

그리고 나서 CS에 진입한다

나올 떄 flag[i]=false로 놓는다.

 

이 코드도 문제가 발생할 수 있다. 

flag[i]=true 라인까지 실행하고 프로세스 j에게 CPU를 뺏겼다고 가정하면 

프로세서 j도 flag[j]=true로 놓는다. 

flag[i]==true, flag[j]==true 지만 while에 들어가서 아무도 CS에 들어가지는 못하는 상황이 발생한다

 

이 알고리즘도 Mutual exclusion은 만족하지만 progress를 만족하지는 않는다 

 

알고리즘3 - 피터슨의 알고리즘 

 

turn과 flag 변수를 모두 사용한다 

프로세스i가 CS에 들어가고자 할 때 먼저 flag[i]=true로 놓는다 

turn=j로 상대방 편으로 놓는다 

while(flag[j]&&turn==j); 로 상대방의 flag가 true고 turn==j인 경우에만 while에서 기다린다 

CS에 들어가고 나서는 flag[i]=false로 놔서 상대방이 들어갈 수 있게 해준다 

 

이 방법은 중간 어느 라인에서 CPU를 빼앗기더라도 프로그램적 해결법의 3가지 조건을 만족한다

(둘이 동시에 들어가있지 않으면서, 아무도 없으면 들어갈 수 있고, 특정 프로세스만 오래 기다리지 않음)

 

Busy Waiting 문제점이 있다 . spin lock이라고도 한다 .

프로세스 i가 CS에 있을 때 프로세스 j한테 CPU가 넘어가면 프로세스 j는 CPU를 갖고있는 시간동안 계속 while에 갇혀서 기다리기만 해야 한다. 

 

Synchronization hardware 

하드웨어적으로 해결하면 간단하게 해결할 수 있다

Test_and_set 명령을 지원하면 된다. 

 

데이터를 읽는것과 쓰는것을 하나의 명령어로 처리할 수 없기 때문에 이제까지 문제가 있었던 것이다 

데이터 읽기 쓰기를 하나의 instruction으로 해결할 수 있으면 하나의 instruction 도중에는 CPU를 뺏기지 않으니까 문제가 해결됨 

 

test_and_set(a)는 a의 값을 읽고 세팅하는 것을 하나의 명령어로 처리한다

Test_and_set(a)를 하면 a 값을 읽고 1로 세팅한다

원래 0이면 0을 읽고 1로 세팅하고 원래 1이어도 1을 읽고 1로 세팅한다

 

test_and_set이 지원된다면 소프트웨어적으로 이렇게 간결하게 코드를 짤 수 있다 

while(test_and_set(lock))에서

lock==0이었다 -> 아무도 CS에 들어가있지 않다 -> 내가 lock을 걸고 들어가면 된다.

이 떄 test_and_set으로 읽어온 값은 0 (lock==0)이므로 . 그리고 lock은 1로 바뀐다. 

lock==1이었다 -> 누군가 lock을 걸어놓음 -> test_and_set(lock)리턴값 1이 읽힘 -> while을 돌면서 기다림

 

CS에서 빠져나올 때 lock=false로 풀어주면 while에서 돌고 있던 다른 프로세스가 들어갈 수 있다.

 

Semaphores

앞의 방식들을 추상화시킨 것임 

object와 operation으로 구성되는 추상 자료형 

 

Semaphore S는 

- 정수값을 갖는다 (자원의 개수)

- 아래의 두가지 atomic 연산으로만 접근 가능하다 

 

 

P 연산

Sempahore 변수를 획득하는 연산이다(공유데이터 획득) : 자원의 개수 S 감소 

S<=0인 동안에 while에서 기다린다 (자원이 없음)

S>0이 되면 (자원이 다시 생기면) 자원을 얻는다

자원이 없을 때 P 연산을 하면 while문을 돌아봤자 자원이 없어서 계속 기다리기만 하는 busy waiting 발생 

 

V 연산

사용 후 반납하는 연산이다 : 자원의 개수 S 증가 

 

Semaphore를 이용해 lock을 걸고 푸는 것을 제공할 수 있다

S가 1일때 P 연산 : lock을 건다, S가 0일떄 V 연산 : lock을 푼다 

공유자원을 획득하고 반납하는 것을 Semaphore가 처리해준다.

 

 

CS에 접근하는 코드에서 semaphore 적용

mutex값을 1로 놓는다 : 프로세스 1개가 CS에 들어갈 수 있다 

CS에 들어갈 떄 P(mutex), 나올 떄 V(mutex)를 해준다.

프로그래머는 P, V 연산만 해주면 되고 시스템이 알아서 P,V를 구현하면 된다.

프로그래머는 훨씬 간단하게 프로그램을 작성할 수 있다.

 

 

Block and WakeUp 구현 

busy wait은 비효율적이라 block and wakeup방식으로 구현할 수 있다.

block and wakeup 방식을 sleep lock이라고 한다 (spin lock의 반대)

sleep lock 방식에서는 프로세스가 공유데이터를 못 얻는 상황이면 프로세스가 blocked 상태가 된다 

blocked 상태는 blocked된 원인이 해결되면 다시 ready queue로 돌아올 수 있다 

blocked 되고 CPU 반납하고 잠들어 있다가 공유데이터를 얻을 수 있게 되면 다시 ready queue로 들어와 CPU를 얻고 공유데이터를 얻는다.

 

semaphore 선언
PCB가 큐에 줄서있는다

semaphore는 변수값과 semaphore 때문에 잠들어있는 프로세스들을 연결하기 위한 큐를 갖는다 

semaphore를 얻을 수 없으면 그 프로세스를 block 시킨다 

그리고 semaphore를 갖고 있던 프로세스가 반납하면 block된 프로세스 중 하나를 깨운다(wakeup)

 

block and wakeup 방식일 때 P와 V연산이 구현되는 방식

P연산 

자원의 여분이 있다 : 자원을 획득

자원의 여분이 없다 : 대기 리스에 이 프로세스를 추가하고 block된다

 

V연산 

자원을 다 쓰고 나서 반납한다 

자원을 반납하고 끝나는게 아니라, 이 자원을 기다리면서 잠들어있는 프로세스가 있으면 깨워준다 

 

P 연산을 보면 일단 S.value--를 하고 잠든다 

V 연산에서 S.value<=0이라는 조건은 내가 자원을 반납 (S.value++) 했음에도 S.value<=0라는 것은 다른 프로세스가 이 자원을 기다리며 잠들어 있다는 것이다. 

예를 들자면 S.value=1로 시작

P1이 P연산 -> S.value=0 -> 공유자원 획득 

P2가 P연산 -> S.value=-1<0 -> block 

P1이 V연산 -> S.value = 0 <=0 -> P2가 block 되어 있으므로 리스트에서 제거하고 wakeup(P1)

 

즉 여기서 S.value는 자원의 개수를 세는 의미와는 다르다

S.value <= 0 : 누군가 자원을 기다리고 있다 

S.value > 0 : 자원의 여분이 있어서 누군가 사용하고 있다

 

Busy wait VS Block & WakeUp

보통은 Block & WakeUp이 더 효율적이다 

하지만 Block & WakeUp 방식도 overhead가 있다. 프로세스의 상태를 ready -> block으로, block -> ready로 바꿔줘야 한다

CS의 길이가 길다 : Block & WakeUp

CS의 길이가 매우 짧다 : Block & WakeUp의 오버헤드가 Busy wait 오버헤드보다 커질수가 있다 

 

Semaphore의 두가지 종류

  • Binary Semaphore ( = mutex )
    • 0 or 1
    • mutual exclusion (lock/unlock)에 사용 
  • Couting Semaphore
    • 0 이상인 이미의 정수값 
    • resource counting에 사용 

 

DeadLock and Starvation

semaphore 사용 시 원치 않는 문제가 생길 수 있다 

공유자원 2개에 동시에 접근하고 해제하는 상황을 가정(하드디스크 -> 하드디스크 복사)

 

DeadLock

데드락은 둘 이상의 프로세스가 서로 상대방에 의해 충족될 수 있는 이벤트를 무한히 기다리는 현상이다 

1. P0이 P(S) 까지 하고 CPU 뺏김 

2. P1이 P(Q) 하고, P(S) 하려는데 S는 P0이 이미 획득함

3. 기다린다.. 영원히 

P0이 V(S)를 할 때까지 기다려야 하는데 V(S)를 하려면 P(Q)를 해야 하고 P(Q)를 하려면 P1이 V(Q)를 해야 하고 P1이 V(Q)를 하려면 ...... 

 

deadlock 해결방법

자원을 획득하는 순서를 똑같이 맞춰주면 해결할 수 있다 

Starvation 

특정 프로세스들만 자원을 공유하면서 다른 프로세스는 영원히 차례가 오지 않는 문제 

 

 양쪽에 있는 젓가락을 집어야 식사를 할 수 있는 상황 

 

Boundede Buffer Problem 

임시로 데이터를 저장하는 공간 , 즉 버퍼의 크기가 유한한 상황에서 생산사-소비자 문제 

 

생산자는 공유버퍼에 데이터를 만들어서 넣는다 ,소비자는 꺼낸다

 

lock을 거는 이유는 동시에 두 프로세스가 생산이나 소비를 하면 문제가 되기 때문 

 

버퍼가 유한하기 때문에 생기는 문제로는 버퍼가 꽉 차면 생산자 입장에서는 할 일이 없다(사용할 수 있는 자원이 없다)

비어있는 버퍼가 0이면 기다리게 된다. 소비자가 데이터를 꺼내가야 생산자가 일을 할 수 있다 .

 

소비자 입장에서는 데이터가 들어있는 버퍼가 자원이다. 없으면 생산자가 채워줄 때 까지 기다려야 한다. 

 

생산자, 소비자 문제에서는 semaphore를 이용해서 해야하는 일이 2가지가 있다. 

- 공유버퍼에 lock을 걸어 동시에 접근하는 것을 막는 일 

- 버퍼가 꽉 차거나, 비었을 때 자원의 개수를 사는 counting 역할 

 

이 상황에서 

Shared Data : 버퍼 자체 및 버퍼 조작 변수

Synchronization variable 

- 동시 접근을 막기 위해서는 binary semaphore 필요 

- resource count를 위해서는 integer semaphore 필요 

 

생산자 소비자 문제의 Semaphore 구현 

 

 

lock을 걸기 위한 mutex=1 , 채워진 버퍼 개수 full=0, 비워진 버퍼 개수 empty=n으로 초기화 

 

생산자는 P(empty)로 빈 버퍼가 있는지 확인. 없을 경우 기다림 

P(mutex)로 락을 걸고 데이터를 추가하고 V(mutex)로 해제 

작업이 끝나면 V(full)로  찬 버퍼의 개수 증가 -> 기다리는 소비자를 깨움 

 

소비자는 반대로 내용이 차있는 버퍼가 있는지 확인 P(full) 없으면 기다림 

락걸고 할일하고 V(empty)로 빈 버퍼 개수 증가 -> 기다리는 생산자를 깨움 

 

Reader Writer 문제 

DB예서 읽는 프로세스, 쓰는 프로세스가 있음 

- 쓰는 프로세스가 동시에 여럿이 하면 안됨 

- 읽는작업은 동시에 여럿이 해도 됨 

 

쓰는동안에는 lock을 걸어야 하지만 읽고있을때 또 읽으려는 애가 들어오면 같이 읽게 해야함 

그렇지 않고 항상 lock을 걸면 비효율적 

write는 항상 아무도 없을때만 해야함 독점적으로 write를 해야함 

 

 

Writer

DB = 공유데이터 , DB에 lock을 걸기 위한 변수가 db

Writer는 P(db)로 lock을 검. 누군가 사용중이라면 기다려야 함 

다 쓰면 V(db)로 해제 

 

Reader 

Reader도 단순하게 구현하려면 Writer랑 똑같이 구현하면 되지만 비효율적임!

 

lock을 걸긴 해야함. 안 걸면 읽는중에 Writer가 써버릴 수가 있음 

readCount라는 몇명의 reader가 읽고있는지 나타내기 위한 공유변수를 둔다 (모든 reader들이 접근가능)

 

if (readcount==1) P(db) 인 이유는 내가 최초의 reader (아무도 읽고있지 않은 상태에서 도착해서 읽는 상황) 일 때만 lock을 걸어주면 되기 때문이다 

readCount 자체도 공유변수이므로 이 readCount에 대해 lock을 걸기 위한 변수가 mutex임 

readCount를 건드리기 전에 lock을 걸고 작업 후에 해제함 

 

다 읽고 나갈 떄는 내가 마지막으로 나가는 reader라면 if (readcount==0) V(db)로 lock을 풀어준다 

 

즉 리더 라이터 문제에서 

- 공유데이터 : BB 자체, readcount 

공유데이터에 접근할 때는 lock을 걸어준다 

- Synchronization variable 

mutuex : readcount에 lock걸기 위함 

db : DB에 lock 걸기 위함 

 

Starvation

리더가 계속 들어오면 라이터는 스타베이션에 걸릴 수 있다. (계속 기다림)

어떻게 해결할까? 큐에 우선순위를 둬서 Writer가 일정수준 이상 기다리지 않게 우선순위를 점차 증가시킬 수 있겠다. 

신호등이 있는 이유랑 비슷하다 

 

Dinning Philosophers Problem 

생각하고 밥먹는 첵상상

생각하다가 배고프면 내 양쪽의 젓가락을 잡아서 밥을 먹는다 

chopstick은 1로 초기화되어 있다. 즉 한번에 한명만 젓가락을 잡을 수 있다

밥먹거나 생각하는 일을 무한반복한다 

 

위 코드는 deadlock의 가능성이 있다 

모든 철학자가 배고파서 자신의 왼쪽 젓가락을 잡았으면 모든 철학자가 밥을 못먹게 된다 

일단 왼쪽 젓가락을 잡으면 자신이 밥먹을 때 까지 놓지 않기 때문 

 

해결방법

- 4명만 동시에 식탁에 앉을 수 있게 한다

- 왼쪽 오른쪽 젓가락을 둘 다 잡을 수 있을 때만 잡을 권한을 준다 

- 비대칭 : 짝수철학자는 왼쪽, 홀수철학자는 오른쪽 젓가락부터 잡도록 한다 

 

이 코드가 semaphore 철학에 잘 안 맞아 보일 수 있다. 일단 넘어가자 

철학자 i가 하는 일은 젓가락 들고 먹고 내려놓고 생각하는 것 반복이다. 

먹기전에 pickup 호출, 먹고나서 putdown 호출 

 

변수 self는 해당 철학자가 젓가락 2개를 다 잡을 수 있는 상황이어서 잡는 권한을 줄지 말지 결정하는 변수이다 

self[i]=1이면 i번째 철학자가 양쪽 젓가락을 잡을 수 있는 상황인 것 

 

state를 통해 철학자들의 상태를 배고픔, 먹는중, 생각중으로 나눠놓음 

 

mutex는 lock을 위한 값이고 옆의 철학자의 상태를 바꿀 수 있기 때문에 공유변수다. 따라서 lock을 해줘야 한다.

 

pickup에서는 lock 걸고 내 상태를 배고픔으로 바꾸고 내가 젓가락 2개를 다 잡을 수 있는지 테스트한다 test(i)

test 함수는 내 왼쪽, 오른쪽 철학자 둘다 밥먹고있지 않고, 내가 배고픈지 확인한다.

이 3가지를 만족해야 내 상태를 먹는중으로 바꿔주고 먹을 수 있는 권한을 준다 

특이한 점은 self는 0이 초기값이므로 V(self[i])를 하면 값이 1로 바뀌므로 권한이 생기는 것이다

 

만약 test에서 내 양쪽 철학자가 중에 밥먹는 철학자가 있어서 권한을 못 얻고 test가 종료되면 P(self[i])에서  기다린다. 

인접한 철학자가 밥 다 먹을 떄까지 

 

밥 다먹고 putdown 할 때는 내 상태를 다시 생각중으로 바꾸고 내 왼쪽 오른쪽 철학자에 대해 상태를 test 한다

혹시 배고픈데 나때문에 못먹고 있던 건 아닌지

 

Monitor 

Semaphore는 P,V연산으로 코딩을 쉽게 만들어주긴 했지만 문제가 생겼을 때 버그를 잡기가 쉽지 않다 

프로그래머가 실수하면 치명적이라는 것 

 

Monitor는 동시 수행중인 프로세스 사이에서 adt의 안전한 공유를 보장하기 위한 high level synchronization construct이다 

monitor의 procdure를 통해서만 공유데이터에 접근할 수 있게 하는 것 

모니터 안에 공유데이터와 공유데이터를 접근할 수 있는 procedure를 정의해놓는다

그리고 모니터 내부의 procdure는 동시에 여러개가 실행되지 않도록 해놓는 것이다 

이렇게 하면 프로그래머는 lock을 걸 필요가 없다 

모니터가 알아서 하나의 프로세스만 접근할 수 있게 해준다 

 

자원이 몇개인지 세는 기능은 여전히 필요함 

condition variable 사용 

여분이 있으면 바로 접근, 여분이 없으면 x.wait()

접근을 다하고 빠져나갈떄는 x.signal()로 기다리는 애들을 빠져나갈 수 있게 해줌 

 

생산자 소비자 문제를 모니터를 활용하면 이렇게 코딩할수 있다 

모니터로 관리되기 때문에 락을 걸 필요가 없다

semaphore보다 프로그래머 입장에서 이해가 쉽고 lock을 신경쓸 필요가 없다 

 


모니터 요약

모니터를 사용하면 

프로세스1이 모니터에 접근하고 있다가 CPU를 뺏기고 프로세스2가 접근하려고 해도 

프로세스1이 모니터에 active 한 상태로 남아있어서 프로세스 2가 접근하지 못함 

모니터 안의 active한 프로세스의 수가 0이될때 다른 프로세스가 접근할 수 있게 된다 

락을 걸고 풀고 할 필요가 없이 모니터가 알아서 해줌.

모니터 안에 데이터가 있고, 데이터에 대한 연산도 있고.. 객체지향 프로그래밍의 형태다 

 

condition variable을 둘 수 있다.

프로세스를 중간에 잠들게 할 수 있는데 잠든 이유에 따라 변수를 둔다. 

잠든 프로세스를 줄세우기 위한 변수다.

 

x라는 조건을 만족하지 못함 -> x.wait() : x에 가서 줄섬 

x.signal : x에 잠들어있는 프로세스를 깨워줌 

 

semaphore는 이해가 어렵다.  모니터를 사용하면 훨씬 코드가 자연스럽다.

모니터와 semaphore 코드는 서로 쉽게 변환할 수 있다 

 

semaphore에서는 V연산에서 기다리는 프로세스를 깨워주는 역할을 한다. 그리고 공유자원의 개수를 1 늘린다  

이 때 sempahore는 값을 갖기 때문에 값의 변화가 항상 있다 

반면 monitor에서는 full.signal() 연산을 했다고 해서 어떤 값이 증감하는 것은 아니다. 

 full로 인해 잠들어 있는 프로세스가 없다면 full.signal 연산에서는 아무일도 일어나지 않는다 

 

모니터는 동시접근을 모니터 차원에서 아예 막아준다 

semaphore는 자원을 획득하기 위해 프로그래머가 알아서 P,V연산을 해줘야 한다. 

 

식사하는 철학자들 모니터 버전의 코드

철학자가 pickup과 putdown을 하는 연산 자체가 모니터에 정의되어 있다

state는 공유변수다. 왜냐하면 나만 나의 상태를 바꿀 수 있는것이 아닌 인접 철학자들도 바꿀 수 있기 때문이다 

하지만 모니터 안에 pickup, putdown 즉 공유데이터에 접근하는 연산을 선언해서 lock을 걸거나 풀 필요는 없다.

 

pickup

test 조건은 왼쪽 철학자, 오른쪽 철학자가 밥을 안먹고 있고, 나는 배고픈 상태여야 한다 

만족하면 밥먹는 상태가 된다 

self[i].signal()은 i 철학자가 잠들어있으면 깨워주기 위함이다.

pickup을 통해 test가 호출되면 철학자 i는 당연히 배고픈 상태다. 왜냐하면 state[i]=hungry로 놓고 test(i)를 호출하기 때문

test에서 state[i]가 eating이 되면 pickup을 탈출하고 eat()해주면 된다.

아닐 경우 self[i].wait()으로 기다리게 된다 

 

putdown

생각하는 상태로 바뀐다

인접 철학자가 나 때문에 밥을 못먹고 있는지 확인하고 깨워주기 위해 왼쪽 오른쪽 철학자에 대해 test를 호출한다 

이 때는 test에서 state[i]==hungry인지 체크하는 것이 의미가 있다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'운영체제 > 이화여대 강의' 카테고리의 다른 글

Memory Management  (0) 2023.11.17
DeadLock  (9) 2023.11.10
CPU scheduling  (0) 2023.10.16
Process Management  (1) 2023.10.16
Process  (0) 2023.10.10