호우동의 개발일지

Today :

article thumbnail

모니터를 사용하는 이유

  • 세마포 또는 mutex 락을 이용하여 임계구역 문제를 해결할 때 문제점
    → 프로그래머가 세마포를 잘못 사용할 시, 다양한 유형의 오류가 쉽게 발생할 수 있음

  • 이러한 오류 처리를 위해 모니터 사용
    • 전략 : 간단한 동기화 도구를 통합하여 고급 언어 구조물로 제공하는 것 → 모니터
    • 모니터는 근본적인 고급 언어 구조물 중 하나

  • Java, C# 등의 많은 프로그래밍 언어들이 모니터의 개념을 편입시킴

 

 


모니터 사용법(Usage)


ADT와 모니터

  • ADT(추상화된 데이터 형) : 데이터와 해당 데이터를 조작하는 함수들의 집합을 하나의 단위로 묶어 보호
    • 이때 함수의 구현은 ADT의 특정한 구현과는 독립적

  • 모니터 형 : 모니터 내부에 프로그래머가 정의한 일련의 연산자 집합을 포함하는 ADT
    • 이때 모니터 내부에서 상호배제가 보장되어야 한다.
    • 변수들의 선언을 포함하고, 이 변수들의 값은 그 형에 해당하는 한 인스턴스의 상태를 정의
      • 모니터형은 해당 변수를 조작할 수 있는 프로시저/함수 본체도 같이 포함

 


모니터 코드 구문과 개략도

mointor monitor name
{
    /* 공유 변수 선언 */

    function P1(...){
    ...
    }
    function P2(...){
        ...
    }
                .
                .
                .

    function Pn(...){
        ...
    }

    initialization_code(...){
        ...
    }
}
  • 모니터 형의 표현은 다른 프로세스들이 직접 사용할 수 없다.
    → 오직 모니터 내에 정의된 함수만 모니터 내에 지역변수와 형식 매개변수들에게 접근 가능
    • 모니터 구조물은 모니터 안에 항상 하나의 프로세스만이 활성화되도록 보장
      → 프로그래머들은 동기화 제약 조건을 명시적으로 코딩할 필요가 없다.

모니터 개략도
모니터 개략도

 


모니터 구조물 동기화 기법 정의

  • 위에서의 모니터 구조물은 어떤 동기화 기법을 모델링하는데 충분하지 않다.
    → 이를 위해 부가적인 동기화 기법 정의가 필요

  • 동기화 기법들은 condition이라는 구조물로 제공
    • 프로그래머는 동기화 기법 작성이 필요할 때, 하나 이상의 condition형 변수를 정의할 수 있다.
      • ex ) condition x, y;

    • condition형 변수에 호출될 연산은 wait()signal() 밖에 없다.
      • x.wait()를 호출한 프로세스는 다른 프로세스가 x.signal()을 호출할 때까지 일시중지
      • x.signal() : 정확히 하나의 일시 중지 프로세스를 재개
        • 일시 중지된 프로세스가 없다면, 해당 연산은 아무런 효과 없음
          → x의 상태는 연산이 전혀 실행되지 않는 것과 같음
          • cf ) 세마포의 signal() 연산은 항상 세마포의 상태에 영향을 준다.

 


모니터에서 두 프로세스의 동시성

  • 가정
    • x.signal() 연산이 프로세스 P에 의해 호출될 때,
      조건 x와 연관된 일시 중지된 프로세스 Q가 있다고 가정

  • 일시 중지된 스레드 Q가 실행을 재개하면, signal을 보낸 스레드 P는 반드시 대기해야 함
    ← 두 프로세스가 동시에 활성화되는 것을 막기 위해

  • 두 프로세스는 개념적으로 실행을 멈추지 않고 계속할 수 있다. → 2가지 가능성 존재
    • Signal and wait : P는 Q가 모니터를 떠날 때까지 대기 혹은 다른 조건을 기다림
    • Signal and continue : Q는 P가 모니터를 떠날 때까지 대기 혹은 다른 조건을 기다림
      • 문제점 : P가 계속 실행되고 Q가 기다린다면, Q가 원하는 논리적 조건이 참이 아닐 수 있게 됨

    • 두 가지 방법의 절충안 존재
      • 스레드 P가 signal() 연산을 실행하면, 즉시 모니터를 떠나고 Q가 재개됨

 

 


세마포를 이용한 모니터의 구현

  • 각 모니터마다 mutex라는 이진 세마포가 정의되고 초기값은 1
    • 프로세스는 모니터에 들어가기 전 wait(mutex)를 실행
    • 프로세스는 모니터를 나온 후에 signal(mutex)를 실행

  • 모니터 구현 시 signal-and-wait기법을 사용
    • Signaling 프로세스는 next라는 이진 세마포가 추가로 필요함
      • 이때, next 이진 세마포는 0으로 초기화된다.
        ← 실행 재개되는 프로세스가 모니터를 떠나든지,
        wait() 할 때까지 자신이 다시 기다려야 하기 때문

    • Signaling 프로세스는 자신을 중단시키기 위해 next를 사용할 수 있다.
      • 정수형 변수 next_count는 next에서 일시 중지 되는 프로세스의 개수를 세기 위해 제공

 


모니터 안에서 상호 배제를 보장하는 코드 구현

wait(mutex);
        ...
        body of F
        ...
if(next_count > 0) // 일시 중지 중인 프로세스가 있다면
        signal(next); // 그 프로세스를 깨움
else // 일시 중지 중인 프로세스가 없다면
        signal(mutex); // 모니터에서 나옴

 


조건 변수를 세마포로 구현하는 방법

  • 각 조건 x마다 이진 세마포 x_sem와 정수형 변수 x_count를 도입
    • 둘 다 초기값을 0으로 초기화한다.
  • x.wait() 구현
x_count++;
if(next_count > 0)
        signal(next);
else
    signal(mutex);
wait(x_sem);
x_count--;

 

  • x.signal() 구현
if(x_count > 0){
        next_count++;
        signal(x_sem);
        wait(next);
        next_count--;
}

 

 


모니터 내에서 프로세스 수행 재개


모니터 안에서 프로세스가 수행 재개되는 순서

  • 가정
    • 조건 변수 x에 여러 프로세스가 일시 중지되어 있는 상태
    • 어떤 프로세스가 x.signal() 연산을 수행하면, 일시 중지된 프로세스들 중 어느 것이 재개될까?

  • 일시 중지된 프로세스를 선택하는 방법
    1. FCFS(선입 선출) 방법 : 가장 오래 기다렸던 프로세스가 가장 먼저 깨어남
      → But, 많은 경우 이렇게 간단한 스케줄링 기법은 충분치 않음

    2. conditional-wait 구조물 사용 ← FCFS 방법의 단점을 보완

 


Conditional-wait 구조물 사용

  • x.wait(c)의 형태를 가짐
    • c : 정수 수식으로, wait() 연산이 호출될 때 값이 계산됨
      • c의 값은 우선순위 번호(priority number)라고 불림
        • 해당 값은 일시 중지되는 프로세스의 이름과 함께 저장

  • x.signal()이 수행되면 가장 작은 우선순위 번호를 가진 프로세스가 다음번에 수행 재개
  • 이 기법이 사용되는 예시 → ResourceAllocator 모니터
    • ResourceAllocator 모니터 : 한 개의 자원을 여러 프로세스 사이에 할당해 주는 역할을 함
    • 프로세스가 자원을 할당받기를 원할 때 따라야 하는 순서
      • R은 ResourceAllocator형의 인스턴스를 뜻함

    • 1. 각 프로세스가 할당받기 원하는 자원의 최대시간을 정한다.
      2. 모니터는 이 중 가장 적은 시간을 희망한 프로세스에 자원을 할당
R.acquire(t); // 할당받기를 원하는 자원을 사용할 최대 시간(t)를 설정
        ...
access the resource;
        ...
R.release(); 

 

  • 하나의 자원을 할당해 주는 모니터 코드 구현
monitor ResourceAllocator
{
        boolean busy;
        condition x;

        void acquire(int time){
            if(busy) x.wait(time);
            busy = true;
        }

        void release(){
            busy = false;
            x.signal();
        }

        initialization_code(){
            busy = false;
        }
 }

 


모니터 사용 시 발생할 수 있는 문제

  • 하지만, 모니터의 개념은 예시한 순서가 그대로 지켜지는 것을 보장하지 않는다.
  • 다음과 같은 상황에 문제가 발생할 수 있음
    1. 프로세스가 자원에 대한 허락을 받지 않고 자원을 액세스 할 경우
    2. 프로세스가 자원에 대한 허락을 받은 다음 그 자원을 방출하지 않을 경우
    3. 프로세스가 자원에 대한 허락을 받지 않았는데도, 그 자원을 방출할 경우
    4. 프로세스가 자원에 대한 허락을 받고, 방출하지 않은 상태에서 또 그 자원을 요청할 경우

 


모니터 문제 해결 방법

  • 컴파일러 자체는 도움을 줄 수 없다.
  • 자원 액세스 연산 자체를 ResourceAllocator 모니터 내부에 두는 방법
    • 해당 방식을 사용하면, 스케줄링을 모니터 자체의 스케줄러에게 맡기는 것
      → 프로그래머가 코딩한 스케줄링 방식을 사용하는 것이 아님

    • 프로세스들의 올바른 순서 보장을 위해서 검사해야 할 것
      • ResourceAllocator 모니터 자체
      • ResourecAllocator 모니터가 관리하는 자원 사용하는 모든 프로그램

  • 시스템 정상 동작을 알기 위해 2가지 조건을 검사해야 함
    1. 프로세스들이 모니터를 정확한 순서에 맞추어 호출하는지 검사
    2. 비협조적인 프로세스가 모니터 공유 자원을 직접 액세스 하지 않는다는 것을 보장받아야 함.
      • 비협조적 프로세스는 액세스 제어 프로토콜을 사용하지 않음
        → 모니터가 정한 상호 배제 규칙 경로를 무시할 수도 있음
        → 위 2가지 조건을 만족 시, 시간 종속적 오류가 일어나지 않고, 원하는 스케줄링이 지켜짐

  • 이 해결 방식의 한계점
    • 규모가 큰 프로그램 또는 동적인 시스템에서는 비합리적
    • 작은 규모며 정적인 시스템에서는 가능함