본문 바로가기

Computer Science/OS

모니터의 개념과 구조, 구현

모니터 안에 조건 변수라는 것이 있습니다.

 

condition이라는 타입으로 x, y를 사용합니다.

약간 변수와 다릅니다.

 

변수는 값을 담는 그릇인데 

condition 타입은 값을 담기위한 용도가 아니고 프로세스를 변수에서 기다리게 하는 효과 입니다.

 

목적은 프로세스 동기화 입니다.

 

조건 변수의 필요성

바쁜 대기와 무한대기를 방지 합니다.

 

지난번에 본 모니터 그림에서 x,y를 넣은 것입니다.

한번에 하나만 들어가 있으니 나머지는 앤트리 큐에 들어가 있는 것을 볼 수 있습니다.

 

x.y를 보면 프로세스 안에 들어와 있긴 한데

대기하고 있는 것입니다.

 

이미 모니터 안에 들어와 있기 때문에 엔트리 큐를 한번 거친 것입니다.

 

 

고객센터에 들어오는 전화를 줄로 세웁니다.

 

상담원은 전화를 받게되는데 동시에 전화걸면 충돌이 발생합니다.

 

상담원이 줄에서 꺼내는 것인데, 

 

줄에 들어갈 때  lock을 걸어야 합니다.

왜냐? 상호배제가 필요하니깐요

2사람이 동시에 들어오면 안되니(같은 번호표를 가지면 안되니) 1줄로 세워야 합니다.

 

뺄 때는 상담원이 같은 사람을 2명의 상담원이 꺼낼 수 있기 때문입니다.

 

줄에대한 상호배제는 모니터로 해결이 가능합니다.

한번에 하나만 들어갈 수 있기 때문입니다.

 

근데 이 상황에서 모니터가 왜 필요한가요?

넣을 때, 뺄 때 문제가 발생하기 때문입니다.

 

ex)

상담원이 3명의 고객을 꺼낼려고 lock을 잡았는데(lock을 걸었다) 줄이 비어있었습니다.

누군가가 들어가려 하더래도 lock이 걸려있기 때문에 못들어 갑니다.

그러면 영원히 모니터가 멈춰버립니다.

 

그러면 상담원이 줄이 비었는지 계속 확인해보면 해결이 될 겁니다!

그런 행동을 하면 busy waiting이 걸리게 됩니다.

 

 

조건 변수의 필요성

왼쪽의 예제는 우리가 알고 있는 세마포 입니다.

while문에서 바쁜 대기가 나타납니다.

 

오른쪽을 봐봅시다.

P()에서

s = 0이면 조건변수에 들어가서 대기합니다.

s = 0이 아니면 정상작동 합니다.

 

V()에서 

s를 하나 늘리고 조건변수를 수행 합니다.

"너 나와도 되"

 

즉, while문에서 계속 채크하면서 기다리는 게 아니고 

 

sIsPositive에서 조건이 만족되었는가를 채크 합니다. => (채크를 하지 않습니다!!!)

조건이 만족되면 깨어날에서 기다립니다.

 

signal에서 조건이 만족되는 순간 시그널을 보내줍니다.

신호를 받으면 하나가 깨어 납니다.

 

ex)

모니터에 어떤 프로세스가 와서 P()가 왔는데 자원을 차지하지 못해서 기다리게 되었습니다.

그럼 P()를 깨우는건 누구일까요?

누군가가 V()를 해줘야 합니다.

즉, 누군가가 안에 들어있다는 뜻입니다.

 

안에 들어가 있는 것이 signal을 안해준다면 조건변수에서 기다리는 것은 영원히 기다리게 됩니다.

 

 

모니터 내부입니다.

  • 들어가는 큐를 거쳐서 모니터안에 들어갑니다.
  • 들어가서 안에 누가 돌고 있으니 하나에 한번에 못돕니다.
  • wait조건 변수에 들어가서 기다립니다.
  • signal을 받아서 깨어납니다.
  • 근데 원래 돌던애도 있고 signal을 받아서 깨어난 애도 있을 것입니다. 그럼 2개가 동시에 돕니다.

 

 

  • 모니터 안에는 조건 변수와 프로시저가 있습니다.
  • 한 순간에 하나의 프로시저만 공유 데이터를 사용할 수 있습니다.
  • 준비 큐에서 모니터가 들어오라고 할 때까지 기다립니다.

 

4번입니다.

조건 변수가 들어가면 컴파일러가 할 일이 더 많아집니다.

 

동기화는 시간을 맞추는 것입니다.

"너 다음에 나 들어갈 깨"

누군가가 줄에 들어오면 깨워주는 것이 동기화 입니다.

 

  • 초기에는 in_use =0으로 자원이 사용 가능한 상태이기에 프로세스 수행(자원 사용). 또 in_use=1로 바꿔 사용 중 알림(❷) 후 리소스 사용
  • 다른 프로세스가 자원 사용 중이라면, in_use=1이므로 wait 연산 호출하여 프로세스 대기(❶).
    이때 대기하는 프로세스에 신호signal가 오면 in_use=1로 바꾸어 사용 중 알림 후  리소스 사용
  • 자원 사용 끝나면 in_use=0으로 바꿔 사용 가능 알림 (❸).
  • 준비 큐에서 대기 중인 프로세스에 signal 보냄( ❹). 

 

 

is_free라는 조건 변수 밑에는 기다리고 있는 애들이 있을 것입니다.

맨 왼쪽이 살아나서 모니터를 실행합니다. 

 

그럼 signal을 받으면 어디부터 다시 실행할까요?

wait(is_free)라고 생각했습니다.

틀렸습니다, wait(is_free)를 하면 줄에 다시 꺼내와서 다시 들어가겠다는 의미 입니다.

 

in_use = 1; 을 실행합니다.

내가 사용할 꺼니깐 사용중이라는 상태를 표시하고 사용하면 됩니다.

 

 

 

ex) 자원을 사용하려는 프로세스가 많이 없습니다.

만약 프로세스가 1개이고

get_resource()를 실행하고 return_resource()를 실행하는 상태를 설명해 보세요

  • get_resource()를 실행하고 
  • in_use=1;로 새팅하고 임계영역에 들어갑니다.
  • return_resource()를 실행하는데
  • in_use=0;으로 만들고
  • 혹시 누군가 기다리고 있을 수 있으니깐 signal(is_free)를 합니다.

 

 

 

여기서 필요한것은 ProducerConsumer 입니다.

집어넣고 빼내는 것을 하는것에 대해 lock이 필요합니다. - producer, consumer에 모니터를 적용해야 합니다.

왜냐하면 add와 remove를 1개만 실행해야 하기 때문입니다. 그걸 세련된 모니터로 구현한다는 것 입니다.

 

모니터를 해야하는 이유

  • lock을 안하면 집어 넣을 때 같은 것을 같은 자리에 넣을 수도 있고
  • 뺄 때도 같은 것을 뺄 수 있습니다.
  • 즉, 상호배제를 해야 합니다.

구성에 대해 설명해 봅시다.

  • 공유 데이터 itemCount
  • 조건 변수 full, empty
  • add(), remove() 메소드가 있습니다.

 

add()는 item을 모니터 내부에 저장합니다.

remove()는 item을 모니터 내부에서 꺼냅니다.

 

사실

여기 까지만 해도 모니터의 기능을 합니다.

 

하지만 조건변수가 필요한 이유는 상호배재와 바쁜 대기를 해결하기 위함 입니다.

 

empty 조건 변수를 넣어서 

버퍼 사이즈가 꽉차있으면 wait(full)에 가서 기다리고 있습니다.

내가 remove했는데 모니터가 비어있다면 wait(empty)가 에가서 기다리고 있습니다.

 

add를 하면 버퍼에 하나라도 들어가 있으니

signal(empty) - 비어 있어서 기다리고 있는 것에 신호를 줍니다.

signal(full) - 버퍼가 꽉차서 기다리고 있는 것들에게 신호를 줍니다.

 

문제)

signal후에 signal 보낸 프로세스가 계속 동작 할 수도 있습니다.

코드 위에 뭔가 붙히면 더 동작하겠죠?

 

 

2가지로 문제를 해결 수 있습니다.

Q는 은인입니다.

 

Signal-and-wait

  • Q가 끝날 때까지  P 는 기다림
  • 시그널도 해주고 물에 빠진놈까지 기다립니다.

동그라미가 프로세스 들입니다.

모니터 안에는 하나밖에 점유를 못합니다.

 

여기서 a,b는 조건 변수 입니다.

대기 큐에 대기하고 있습니다.

누군가가 시그널을 당해야지 튀어나올 수 있습니다.

 

signal을 해서 남을 살려준 프로세스가 기다려줄 가 필요합니다.

signal이 보낸 프로세스가 기다리는 큐는 오른쪽 끝입니다.

 

 

Signal-and-continue

  • P가 끝날 때가지 Q가 기다림
  • "signal한 해준 것도 고마워 해야지"
  • Mesa, C#, Java

프로세스 P가 될 수 있는 것은

모니터 안에 들어간 애 입니다.

자기 자신을 계속 실행을 하고 

밖에서 들어온 애들이 있고 wait에서 다시 모니터에 들어가고 싶은 애들이 있습니다.

 

Q는 a.q에 들어가 있습니다.

 

P가 signal을 보내면 a.q에서 다시 모니터에 들어가는 큐 줄에 들어갑니다.

 

 

이런것을 어떻게 구현할까요?

변수가 3개 필요합니다.

 

하나의 모니터만 차지하게 하는 - 세마포 mutex

 

signal로 나갈 때 누구를 먼저 들어오는지 보면 signal(next)를 보내고 있는 애들을 먼저 풀어줘야 합니다.

next_count = "next에서 기다리고 있는 것이 있다면" 이라는 뜻입니다.

 

mutex의 초기값이 1이여야 합니다. 왜냐?

모니터에 들어갈 수 있는 자원의 수이기 때문입니다.

 

 

핵심

  • wait는 세마포를 만들어서 세마포에서 기다리는 것 입니다.
  • signal은 세마포에 대해서 signal을 보냅니다.
  • 조건변수는 내부적으로 세마포로 구현되어 있습니다.

모니터 조건변수에서 기다리는 것은 세마포 값을 0으로 해야 wait(x_sem)을 했을 때 바로 기다릴 수 있습니다.

 

세마포는 병렬 프로그램을 가정하고 만듭니다.

모니터에서의 signal은 signal(x_sem)을 보내면 내가 wait()해야 합니다.

내가 wait(next) 할 세마포를 만든 것입니다.

 

 

x_sem의 초기값도 0으로 해놓았습니다.

 

wait()

  • 내가 기다리려면 남을살려줘야 합니다. 왜냐? 모니터에는 하나는 꼭 돌아가야 합니다.
  • 컴파일러의 부담이 모니터에서는 하나의 프로세스만 돌아가기위한 추가 작업입니다.

 

signal()

  • 내가 남을 살려주면 내가 wait에 가서 기다리게 되야 합니다.

 

조건 변수에서 기달릴 때

우선순위를 줄 수 있습니다.

 

ex)자원을 사용하는 시간에 따라 우선순위를 부여한다.

 

time을 줘서 기다리게 합니다.

 

왼쪽은 세마포 오른쪽은 모니터

모니터는 한꺼풀 잠겨있는데

 

세마포는 프로세스가 직접 가져다가 씁니다.

 

세마포 vs 모니터 

 

signal연산에서 다릅니다.

세마포는 V연산에서  무조건 세마포를 증가시킵니다.

세마포는 병행 수행 합니다.

 

모니터는 signal연산시 두 프로세스중 하나만 실행합니다.

 

wait연산에서 다릅니다.

모니터의 wait는 무조건 대기상태가 됩니다.

세마포는 공유자원을 점유하면서 계속 실행할 수 있습니다.

728x90