동기화 방법(뮤텍스 락 , 세마포, 모니터)

Chan Young Jeong·2023년 3월 4일
0

운영체제

목록 보기
8/11
post-thumbnail

프로세스의 동기화는 어떻게 이루어질까요? 임계 구역에 하나의 프로세스만 진입하게 하고, 올바른 실행 순서를 어떻게 보장할 수 있을까요?

동기화를 위한 대표적인 방법인 뮤텍스 락, 세마포, 모니터에 대해 알아보겠습니다.

뮤텍스 락

이 동기화 문제를 한번 옷 가게 탈의실에 비유해보겠습니다. 손님들은 탈의실이라는 자원을 이용하고 탈의실에는 한 사람씩만 들어갈 수 있습니다. 그렇다면 손님은 프로세스, 탈의실은 임계 구역에 비유할 수 있습니다. 이런 상황에서 탈의실이 비었는지 안 비었는지를 알 수 있을까요? 바로 탈의실 열어 보고 자물쇠가 걸려 있으면 기다리고 안 걸려 있으면 들어가면 됩니다.

바로 여기서 자물쇠를 코드로 구현한 것이 뮤텍스 락입니다.

뮤텍스 락은 하나의 전역 변수와 두 개의 함수로 구현할 수 있습니다.

  • 자물쇠 역할 : 프로세스들이 공유하는 전역 변수 lock
  • 임계 구역을 잠그는 역할 : acquire() 함수
  • 임계 구역의 잠금을 해제하는 역할 : release() 함수

acquire 함수

acquire함수는 프로세스가 진입하기 전에 호출하는 함수입니다. 임계 구역을 다른 프로세스가 사용 중이면 열릴 때까지(lock이 false가 될 때까지) 반복적으로 임계 구역을 확인하고 만약 임계 구역이 열려 있다면 임계 구역에 들어가고 임계 구역을 잠그는(lock을 true로 바꾸는) 함수입니다.

acquire(){

    while(lock == true)
        ; // Busy Waiting

    lock = true;

}

release 함수

release 함수는 임계 구역에서 작업이 끝나고 호출하는 함수입니다. 즉 , 현재 잠긴 임계 구역을 열어 주는 (lock을 false로 바꾸는) 함수라고 보면 됩니다.

release(){

    lock = false;

}

이렇게 되면 임계 구역 전후로 acquire 함수와 release함수를 호출함으로써 하나의 프로세스만 임계 구역에 들어갈 수 있습니다.

acquire() // 자물쇠 확인, 잠겨 있으면 기달리고, 그렇지 않으면 잠그고 들어가기


/*
임계 구역에서의 작업진행
...

*/

release() // 자물쇠 반환

Busy Waiting

코드를 보면

acquire(){

    while(lock == true)
        ; // Busy Waiting

    lock = true;

}

반복적으로 lock을 확인하는 걸 볼 수 있습니다. (lock == true인지 계속해서 검사) 마치 탈의실 문을 계속해서 잠겨 있는지 확인해보는 것과 같습니다. 이런 대기 방식을 Busy Waiting이라고 합니다.

세마포

세마포는 뮤텍스 락과 비슷하지만 좀 더, 일반적인 방법입니다. 뮤텍스 락은 하나의 공유 자원에 접근하는 프로세스를 상정하는 방식입니다. 즉 탈의실이 하나 있는 경우를 가정한 경우입니다. 하지만 탈의실이 여러 개 있는 상황처럼 공유 자원이 여러 개 있을 경우 여러 개의 프로세스가 각각 공유 자원에 접근이 가능해야 합니다.

세마포에도 이진 세마포와 카운팅 세마포가 있지만 이진 세마포는 뮤텍스 락과 비슷하기 때문에 카운팅 세마포에 대해서만 다루겠습니다.

세마포는 옛날 철도 신호기에서 유래한 단어입니다. 기차 신호기가 내려가 있으면 가도 멈춰야 하고 기차 신호기가 올라가 있으면 가도 좋다라는 신호로 간주하고 움직입니다. 즉, 프로세스 또한 임계구역 앞에서 멈춤 신호를 받으면 잠시 기다리고, 가도 좋다라는 신호를 받으면 임계구역에 들어갈 수 있게 됩니다.

  • 전역변수 S: 임계 구역에 진입할 수 있는 프로세스의 개수
  • wait함수 : 임계 구역에 들어가도 좋은지, 기다려야 할지 알려주는 함수
  • signal함수 : 임계 구역 앞에서 기다리는 프로세스에게 가도 좋다고 신호를 주는 함수

wait 함수

wait(){

    while(S <= 0) // A
    ;             // B
    S -- ;        // C

}
  • A 부분 : 임계 구역에 진입할 수 있는 프로세스의 수를 확인합니다. 만약 0이하라면

  • B 부분 : 사용할 수 있는 자원이 있는지 반복적으로 확인합니다.

  • C 부분 : 임계 구역에 진입할 수 있는 프로세스의 수가 1개 이상이면 S의 값을 1 감소시키고 임계 구역에 진입합니다.

signal 함수

signal(){

    S ++ ; 

}

signal 함수는 임계 구역에서의 작업을 마친 뒤 S 값을 1 증가 시킵니다.

Busy Waiting

세마포도 마찬가지로 앞에서 뮤텍스 락처럼 공유 자원이 없는 경우 프로세스는 무작정 무한히 S를 확인해야 합니다. 바쁜 대기를 계속해서 하면 그 시간에 CPU는 더 생산성 있는 작업을 할 수 있을텐데, CPU 자원을 낭비하게 됩니다.

따라서 이를 위해 세마포는 다른 방식을 사용합니다.

wait 함수는 만일 사용할 수 있는 자원이 없다면 해당 프로세스를 대기 상태로 만들고, 그 프로세스의 PCB를 세마포를 위한 대기큐에 집어 넣습니다.

그리고 다른 프로세스가 임계구역에서 작업이 끝나고 signal 함수를 호출하면 대기 중인 프로세스를 대기 큐에서 삭제하고 다시 프로세스를 준비 상태로 변경한 뒤 준비 큐로 옮겨 줍니다.

<프로세스 PCB와 프로세스의 상태에 대해서는 참고>

wait(){
    S -- ;
    if(S < 0){
        add this process to Queue
        sleep();
    }
}
signal(){
    S ++;
    if(S <= 0){
        remove a process P from QUeue
        wakeUp(p)
    }

}

지금까지는 세마포를 이용해서 상포배제를 위한 동기화에 대해 알아보았습니다. 이번에는 세마포를 이용해서 프로세스의 실행 순서를 제어하는 방법에 대해 알아보겠습니다.

실행 순서 동기화를 위한 세마포

세마포의 변수 S를 0으로 두고 먼저 실행할 프로세스 뒤에 signal 함수, 다음에 실행할 프로세스 앞에 wait 함수를 붙이면 됩니다.

예를 들어 먼저 실행할 프로세스를 p , 나중에 실행할 프로세스를 q라고 한다면, p가 먼저 실행되면 p가 먼저 임계 구역에 들어가는 것은 자명한 일이고, q가 먼저 실행이 되더라도 wait함수를 만나 기다려야 합니다. 그렇기 때문에 p가 먼저 임계 구역에 들어가고 signal 함수를 호출해서 이후에 q가 실행 될 수 있도록 실행 순서를 제어할 수 있게됩니다.

모니터

세마포는 그 자체로 훌륭한 동기화 도구이지만 매번 임계구역 앞 뒤로 wait과 signal 함수를 붙여 줘야 한다는 불편함이 있습니다. 그리고 코드가 길어지면 당연히 누락하거나 순서를 헷갈리는 경우도 생기게 됩니다. 이에 최근에 등장한 동기화 기법이 바로 모니터입니다.

모니터는 공유 자원과 공유 자원에 접근하기 위한 인터페이스(통로)를 묶어서 관리합니다. 그리고 프로세스는 이 인터페이스를 통해서만 공유 자원에 접근할 수 있습니다.

이를 위헤 모니터를 통해 공유 자원에 접근하고자 하는 프로세스를 큐에 삽입하고, 큐에 삽입된 순서대로 하나씩 공유 자원을 이용하도록 합니다.
즉, 모니터는 프로세스에 접근하기 위한 큐를 만들고(모니터 접근을 위한 큐), 모니터 안에 하나의 프로세만 들어오도록 하여 상호 배제 동기화를 제공합니다.

그리고 모니터 또한 프로세스간 실행 순서를 제어하기 위한 동기화를 지원합니다. 특정 조건을 바탕으로 프로세스를 실행하고 일시 중단하기 위해 모니터는 조건 변수를 이용합니다. 이는 프로세스나 스레드의 실행 순서를 제어하기 위해 사용하는 특별한 변수입니다.

조건 변수로는 wait와 signal 연산을 수행할 수 있습니다.
wait는 호출한 프로세스의 상태를 대기 상태로 전환하고 일시적으로 조건 변수에 대한 대기 큐에 삽입하는 연산입니다. 여기서 모니터에 진입하기 위해 삽입되는 큐와 조건 변수에 대한 대기 큐는 다릅니다. 전자는 모니터에 한 번에 하나의 프로세스만 진입하도로고 하는 큐이고, 후자는 이미 모니터에 진입한 후 실행 조건이 만족될 때까지 잠시 중단되어 기다리기 위해 만들어진 큐입니다.

예를 들어 모니터에 진입한 프로세스가 조건 변수 x.wait을 통해 조건 변수 x에 대한 wait함수를 호출하면 그 프로세스는 일시 중지되어 조건 변수에 대한 큐에 넣어집니다. 그렇게 되면 사용하던 모니터가 비었기 때문에 모니터에 다른 프로세스가 들어올 수 있게 됩니다. 이 프로세스가 x.signal을 통해 조건 변수 x에 대한 큐에 삽입 되어 있던 프로세스가 깨어나 다시 들어올 수 있게 됩니다.
따라서 모니터에는 하나의 프로세스만 들어가 있어야 하기 때문에, wait를 호출했던 프로세스는 signal을 호출한 프로세스가 모니터를 떠난 뒤 실행되거나, signal을 호출한 프로세스의 실행을 일시 중단시키고 자신이 실행된 뒤, 다시 signal을 호출한 프로세스의 수행을 제개합니다.
이렇게 모니터를 이용해 실행 순서를 동기화를 할 수 있습니다.


출처
혼자 공부하는 운영체제 - 강민철 저

0개의 댓글