[OS] Operating System - Synchronization Tools - mutex lock / semaphore

2024. 1. 31. 19:23CS/OS

Synchronization Tools

 

이전 ppt에서 경계구역(critical section)에서 발생하는 문제들을 정리했었다. 이번 내용은 Mutex Locks, Semaphore, Monitor, Liveness 등의 해결 방법에 대해 좀 더 알아보는 시간이었다. 먼저 이번에 등장하는 내용의 단순한 개념은 다음과 같다. ppt의 구성은 4개의 방법을 다루지만 강의가 나눠져 있었으니 먼저 mutex lock과 semaphore에 대해 정리했다.

  • Mutex Locks : 동기화를 위한 가장 단순한 도구
  • Semaphore : mutex lock보다 좀 더 강력하고, 편하고, 효과적인 도구

 

 

Mutex Locks

 

 뮤텍스 락(Mutex Lock)은 동시성 관리를 위해 사용이 된다. Mutual Exclusion의 약자이며, 공유자원에 여러 스레드, 프로세스가 접근하는 것을 방지한다. 공유자원에 동시에 접근하는 것을 방지하여 데이터의 일관성 및 무결성을 유지한다.

 

 뮤텍스 락의 주요 기능은 다음과 같다

  • 상호 배제(Mutual Exclusion) : 공유자원에는 하나의 프로세스, 스레드만 접근이 가능하다. 이를 통해 race condition을 방지한다.
  • 소유권 (Ownership) : 특정 프로세스, 스레드에 소유가 가능하여 해당 소유자만 락을 해제할 수 있다.

뮤텍스 락은 두 가지의 상태를 통해 진행이 된다. 상태는 Lock(Acquire)과 Unlock(Release)이다.

Mutex Lock

 Acquire의 경우 공유자원에 접근하기 전에 뮤텍스 락을 획득하는 것이다. 공유자원에 접근하기 위해 락을 요청했을 때, 락이 이미 다른 프로세스나 스레드에 배정이 되어있는 상황이면 요청을 한 프로세스나 스레드는 대기 상태로 변경되거나 락이 해제가 될 때까지 차단이 된다. 

 

 Release의 경우 스레드가 공유자원에 대한 작업을 완료 한 후 락을 해제하는 과정이다. 해당 과정을 통해 공유자원에 접근할 수 있는 락이 해제가 되면 다른 스레드, 프로세스가 공유자원에 접근이 가능해진다.

Acquire && Release

 

  • acquire()
    • 공유자원에 접근가능한 락을 획득하기 위한 함수이다.
    • available이라는 변수로 락의 상태를 나타내며 true이면 락의 획득이 가능하고, false이면 이미 다른 스레드가 락을 보유한 상태를 의미한다.
    • while 반복문 내부에서 락이 사용 가능해질 때까지 반복적으로 검사하며, 이때의 과정을 busy wait, spinLock이라고 한다
  • release()
    • 락을 해제하고 다른 스레드가 락을 획득할 수 있도록 앞에서 말한 available의 값을 변경한다

Busy waiting - Spin Locks

더보기

멀티 프로그래밍 시스템에서 사용이 가능한 낮은 수준의 동기화 메커니즘이다. 스레드 혹은 프로세스가 조건을 만족할 때까지 조건을 계속 검사하면서 대기하는 방식으로 CPU 시간을 소모하면서 대기 상태의 스레드를 실행할 준비를 한다.

 

Busy waiting의 경우 멀티프로그래밍을 진행하면서 특정 조건이 참이 될 때까지 기다리는 동안을 의미한다.

Spin Locks의 경우 busy waiting을 구현하는 방법이다. 스레드가 락을 얻을 때까지 락의 상태를 체크하며 상호배제(mutual exclusion)를 구현하기 위해 사용되는 특정 메커니즘이다. Spin locks의 경우 다음과 같은 특징들이 존재한다.

 

  • 낮은 오버헤드 : 락을 주고받는 시간이 짧은 경우 사용이 된다. 이 경우 문맥 교환의 오버헤드를 피할수 있기에 유용하다
  • 단순성 : 구현의 로직이 단순하다. 다른 락을 구현하는 매커니즘에 비해 간단하게 짧은 시간 내에 구현할 수 있다.
  • 리얼타임 시스템 : 락을 획득하려는 시간이 예측 가능해야하는 리얼타임 시스템에 유리하다

다만 락을 획득할 때까지의 Cpu 사이클 소모로 락의 획득이 늦어지면 그만큼 자원의 낭비가 심해질수 있으며, 우선순위가 낮으면서  락을 점유하고 있는 경우 시스템 성능에 영향을 줄 수 있다.  

 

Semaphores 

 

세마포어는 신호장치, 신호기이다. 이전 뮤텍스락에서 발전한 형태이며 고급 동기화 매커니즘이다. 세마포어의 경우 공유 자원을 동시에 접근할 때 발생하는 경쟁 조건(race condition)을 방지하기 위해 사용한다. 세마포어의 특징은 다음과 같다.

  • 정수 타입의 변수로 구현
    • 정수 값을 가지며 해당 값은 시스템에서 동시에 접근이 가능한 자원의 수를 의미한다
    • 세마포어의 값에 0이면 해당 자원은 모두 사용 중이며, 추가적인 프로세스/스레드는 자원의 할당이 해제될 때까지 기다려야 한다
  • 원자적 연산
    • 원자적 연산의 형태를 띠고 있다. wait(), signal() 두 개의 연산으로 이루어져 있다. 해당 연산들은 분할 및 실행 후 중단이 불가능하다. 
    • wait()
      • 세마포어의 값을 감소시킨다. 이미 0이라면 해당 연산을 호출한 프로세스, 스레드를 대기 형태로 전환한다.
    • signal()
      • 세마포어의 값을 증가시킨다. 해당 연산을 통해 대기 중인 프로세스, 스레드를 깨울 수 있으며, 대기상태에서 전환된 프로세스, 스레드는 자원을 사용할 수 있다

wait와 signal

 

세마포어는 바이너리 세마포어와 카운팅 세마포어로 나눠진다. 바이너리의 경우 0과 1의 값만 가질 수 있는 세마포어로 뮤텍스 락과 유사하게 작동을 한다. 하나의 리소스에 대해 접근을 제어할 때 사용이 된다. 카운팅의 경우는 0 이상의 값을 가질 수 있는 세마포어이다. 0 이상의 값을 가질 수 있기에 여러 개의 리소스에 대한 접근을 제어할 수 있다.

 

세마포어의 경우 뮤텍스락보다 더 강력하기에 뮤텍스락의 특징인 상호배제에 이어서 자원의 카운팅 및 동기화가 가능하다.

  • 상호 배제(mutual exclusion)
  • 자원 카운팅(resource counting) : 제한된 수의 리소스가 있는 경우, 리소스의 사용 가능 여부를 추적할 수 있다.
  • 동기화(synchronization) : 여러 프로세스나 스레드가 특정 시점에 동시에 도달할 수 있도록 관리한다.

세마포어는 간단하게 다양한 동시성 문제를 해결하는 데 사용이 된다. 바이너리의 경우 뮤텍스 락으로 사용이 가능하고, 숫자를 변경하여 카운팅 세마포어로 사용하면 여러 개의 리소스를 관리할 수 있다. 다만 사용에 주의하지 않는다면 교착상태(deadlock)와 기아상태(starvation)의 위험이 있다. 따라서 올바르게 구현하고 사용을 해야 한다.

 

더보기

교착 상태(DeadLock)

다중프로그래밍 환경에서 발생하며 두 개 이상의 실행단위(프로세스, 스레드)가 서로 상대방이 보유한 자원을 요구하면서 무한히 대기하는 상황을 의미한다. 모든 관련 프로세스, 스레드가 영원히 진행되지 못하고, 서로가 상대방의 작업이 끝나기만을 기다린다.

 

교착상태가 발생하려면 다음과 같은 조건이 충족되어야 한다.

  • 상호 배제 : 하나의 자원이 비공유 모드로 점유되어야하며, 한 번에 하나의 프로세스만이 그 자원을 사용할 수 있다.
  • 보유 및 대기 : 프로세스가 최소한 하나의 자원을 보유한 상태로, 다른 프로세스에 의해 현재 점유된 추가 자원을 대기하는 상황
  • 비선점 : 자원이 자발적으로 락을 반환하거나 해제될때까지 선점을 할 수 없는 경우이다. 프로세스가 자원을 사용하고 있을 때 다른 프로세스는 자원을 강제로 사용할 수는 없는 상황이다.
  • 순환 대기 : 대기하는 프로세스의 집합에서 서로가 서로의 보유 자원을 대기하는 순환 형태를 의미한다.

해결 방법은 다음과 같다.

  • 예방 : 위의 조건 중 적어도 하나는 절대로 발생하지 않도록 시스템을 설계하는 것
  • 회피 : 교착 상태를 유발할 수 있는 요청을 검사하여 피하는 방법 - 뱅커스 알고리즘
  • 탐지 및 복구 : 교착 상태를 탐지하고, 해결을 위해 프로세스 종료 및 점유된 자원 선점
  • 무시 : 해당 상태를 완전히 무시하고, 발생할 경우 시스템을 재시작하는 것과 같은 대응 - 사용자 대화형 시스템에서 사용