실행 순서 제어 : 무작정 아무 순서대로 실행해서는 안되기에 올바른 순서대로 진행하는 것.
상호 배제(mutual exclusion) :동시에 자원 접근해서는 안되는 것을 동시에 접근하지 못하도록 하는 것.
임계구역 문제 해결 3가지 원칙
임계 구역에 진입하는 프로세스는 ‘내가 지금 임계구역에 있음’을 알리기 위해 뮤텍스 락을 이용해 임계 구역에 자물쇠를 걸어둘 수 있고, 다른 프로세스는 임계구역이 잠겨 있다면 기다리고, 잠겨 있지 않다면 임계구역에 진입할 수 ㅇ.
뮤텍스 락의 매우 단순한 형태는 하나의 전역변수와 두 개의 함수로 구현할 수 ㅇ.
acquire함수는 프로세스가 임계구역에 진입하기 전에 호출하는 함수. 만일 임계 구역이 잠겨 있다면 임계구역이 열릴 때까지(lock이 false가 될 때까지) 임계구역을 반복적으로 확인하고, 임계구역이 열려 있다면 임계 구역을 잠그는 (lock을 true로 바꾸는 )함수.
release 함수는 임계구역에서의 작업이 끝나고 호출하는 함수. 현재 잠긴 임계 구역을 열어주는 함수( lock을 false로 바꾸는).
acquire함수가 잠겨 있다면 프로세스는 반복적으로 lock을 확인하는데 마치 탈의실 문이 잠겨 있는지 쉴 새 없이 반복하며 확인하는 것과 같다. 이런 대기 방식을 바쁜 대기(busy wait)이라 한다.
세마포(semaphore)는 뮤텍스 락과 비슷하지만, 조금 더 일반화된 방식의 일반화 도구. 뮤텍스 락은 하나의 공유 자원에 접근하는 프로세스를 상정한 방식이다. 즉 탈의실이 하나 있는 경우를 가정하고 만든 동기화 도구이지만 탈의실이 여러 개 있는 상황처럼 공유 자원이 여러 개 있을 경우 여러 개의 프로세스가 각각 공유자원에 접근이 가능해야 한다. 예를 들어 탈의실이 세 개면 탈의실 자체에는 한 사람만 들어갈 수 있지만 각 탈의실에 세 명이 동시에 사용할 수 있다. 그리고 하나의 프린터를 사용할 수 있는 프로세스(사람)은 하나지만, 총 세 개의 프로세스(사람이)가 세 대의 프린터(공유자원)을 이용할 수 있다. 이처럼 세마포는 공유 자원이 여러 개있는 상황에서도 적용이 가능한 동기화 도구.
(엄밀히 말하면 세마포 종류에도 이진 세마포(binary semaphore)와 카운팅 세마포(counting semaphore)가 있지만, 이진 세마포는 뮤텍스 락과 비슷한 개념이므로 여기선 여러 공유 자원을 다룰 수 있는 카운팅 세마포를 다룸.)
→ 뮤텍스 락은 임계구역을 잠금으로써 상호 배제를 위한 동기화 도구이며, 하나의 공유자원에 접근하는 프로세스 방식이고, 세마포는 뮤텍스 락과 비슷하지만 공유 자원이 여러 개 있는 임계구역 문제도 해결할 수 있는 동기화 도구이다.
세마포는 철도 신호기에서 유래한 단어, 기차는 신호기가 내려가 있을 때는 ‘멈춤’으로 간주하고 잠시 멈추고, 신호기가 올라가 있을 때는 ‘가도 좋다’는 신호로 받아들여 다시 움직이기 시작한다. 세마포는 이와 같이 ‘멈춤’과 ‘가도 좋다’는 신호로서, 임계 구역을 관리한다.
세마포는 상호배제를 위한 동기화 방법만 있는 것이 아님. 실행 순서 제어(’특정 조건이 만족되어야만 실행할 수 있는 상황에서 올바른 순서대로 실행하게 하는 것’)를 위한 세마포도 있다. 세마포의 변수 S를 0으로 두고 먼저 실행할 프로세스 뒤에 signal 함수, 다음에 실행할 프로세스 앞에 wait 함수를 붙인 는 방법이 있다.
사용할 프로세스 뒤에 wait, signal 함수를 매번 명시하는 것도 번거롭고, 잘못된 코드로 인해 예상치 못한 결과가 나올 수 있다는 불편함을 세마포는 가지고 있다. 이에 등장한 것이 모니터이다.
모니터(monitor) : 공유 자원과 공유 자원에 접근하기 위한 인터페이스(통로)를 묶어 관리하고 프로세스는 반드시 인터페이스를 통해서만 공유자원에 접근 가능. 이를 위해 모니터를 통해 공유자원에 접근하고자 하는 프로세스를 큐에 삽입하고, 큐에 삽입된 순서대로 하나씩 공유자원을 이용하도록 한다. 즉, 모니터는 공유자원을 다루는 인터페이스에 접근하기 위한 큐(모니터에 진입하기 위한 큐)를 만들고, 모니터 안에 하나의 프로세스만 들어오도록 해 상호배제를 위한 동기화 제공.
세마포와 같이 모니터도 실행 순서 제어를 위한 동기화도 제공한다. 특정 조건을 바탕으로 프로세스를 실행하고 일시 중단하기 위해 모니터는 조건 변수를 사용.
조건변수(condition variable) : 프로세스나 스레드의 실행 순서를 제어하기 위해 사용하는 특별한 변수.
조건변수로는 wait함수와 signal함수 연산을 수행할 수 있다.
*️⃣ 주의할 점 : 모니터에 진입하기 위해 삽입되는 큐(상호배제를 위한 큐)와 wait함수가 호출되어 실행이 중단된 프로세스들이 삽입되는 큐(조건변수에 대한 큐)는 다르다.
모니터 안에는 하나의 프로세스만 있을 수 있다. 따라서 wait함수를 호출했던 프로세스는 signal을 호출한 프로세스가 모니터를 떠난 뒤 실행되거나, signal을 호출한 프로세스의 실행을 일시 중단하고 자신이 실행한 뒤(wait을 호출한 프로세스) 다시 signal을 호출한 프로세스의 수행을 재개한다.
식사하는 철학자 문제 : 교착 상태를 설명하기 위한 아주 고전적이고 재미있는 문제 상황. 이 문제는 교착 상태가 어떤 상황에서 왜 발생하는지, 나아가 교착 상태를 어떻게 해결할 수 있는지를 엿볼 수 있는 가상의 시나리오.
<aside> 🗣 동그란 원탁에 다섯 명의 철학자가 있는데, 이 철학자들 앞에는 식사가 있고, 철학자들 사이 사이에 식사에 필요한 포크가 있다. 그리고 이 철학자들 앞에 있는 식사는 두 개의 포크로 먹을 수 있는 음식이라고 가정한다면 이 철학자들은 다음과 같은 순서로 식사를 한다.
</aside>
→ 모든 철학자가 동시에 포크를 들면 어떤 철학자도 식사 x , 모두 왼쪽 포크를 들면 모두가 오른쪽 포크 사용 x
—> 모든 철학자는 다른 철학자가 포크를 내려놓을 때까지 기다림. 이렇게 일어나지 않을 사건을 기다리며 진행이 멈춰있는 상태를 **교착상태(deadlock)**이라 함.
식사하는 철학자 문제에서 철학자는 프로세스 혹은 스레드, 포크는 자원, 생각하는 행위는 자원을 기다리는 것에 빗대어 볼 수 있다.