공유 자원 (Shared Resource)
"공유 자원(Shared Resource)"이란 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일들을 의미한다. 말 그대로 공동으로 이용되기에 누가 언제 데이터를 읽고 쓰는가에 따라 결과가 달라질 수 있다. 2개 이상의 프로세스가 공유 자원을 병행하여 읽거나 쓰는 상황을 "경쟁 조건(Race Condition)"이라고 한다. 경쟁 조건이 발생하면 공유 자원 접근 순서에 따라 실행 결과가 달라질 수 있기에 프로세스들의 공유 자원 접근 순서를 정해서 예상치 못한 문제가 발생하지 않도록 해야 한다.
임계구역 (Critical Section)
공유 자원 접근 순서에 따라 실행 결과가 달리지는 프로그램의 영역을 "임계구역(Critical Section)"이라고 한다. 임계구역에서는 프로세스들이 동시에 작업을 해서는 안 된다. 한 프로세스가 임계구역에 들어갔다면 다른 프로세스는 임계구역 밖에서 기다려야 하며 임계구역에 들어간 프로세스가 나와야 밖에 있던 프로세스는 임계구역으로 들어갈 수 있다.
임계구역 문제 해결 방법
임계구역에 문제가 발생했다면 해결하기 위해서는 다음의 세 가지 조건을 만족해야 한다.
- 상호 배제(Mutual Exclusion): 임계구역 내에는 한 번에 하나의 프로세스만 있어야 한다. 즉, 한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구역에 들어갈 수 없다.
- 한정 대기(Bounded Waiting): 어떤 프로세스도 임계구역에 진입하지 못하여 무한 대기하지 않아야 한다.
- 진행의 융통성(Progress Flexibility): 한 프로세스는 다른 프로세스의 진행을 방해해서는 안 된다.
임계구역 문제를 해결하는 가장 간단한 방법은 잠금(lock)을 이용하는 것이다. 프로세스가 임계구역으로 들어가면 임계구역을 잠그고 임계구역에서 나올 때 잠금을 해제하는 것이다. 그리고 잠금 해제와 동시에 동기화 신호를 보내는 것이다.
피터슨 알고리즘과 데커 알고리즘 (Peterson Algorithm & Dekker Algorithm)
피터슨 알고리즘(Peterson Algorithm)은 turn이라고 하는 공유 변수를 하나 더 추가하여 임계구역 문제를 해결한다. turn 변수는 두 프로세스가 동시에 잠금(lock)을 설정하여 임계구역에 들어가지 못하는 상황을 대비한다. 두 프로세스가 동시에 lock을 설정해도 turn을 통해 다른 프로세스에게 양보한다. 즉, turn 변수를 통해 우선권을 관리하는 것이다.
데커 알고리즘(Dekker Algorithm)은 두 개의 프로세스가 동시에 임계구역에 진입하는 경우, 현재 turn이 아닌 프로세스가 대기한다. 즉, 우선권을 교대로 부여하는 것이다.
피터슨 알고리즘과 데커 알고리즘 모두 임계구역 문제 해결 조건 3가지를 만족하지만, 프로세스가 늘어나면 전체 알고리즘이 복잡해지기에 잘 사용되지 않는 방식이다.
세마포어 (Semaphore)
세마포어 알고리즘(Semaphore)은 프로세스가 임계구역에 진입하기 전, 세마포어는 사용 중으로 표기하고 임계구역으로 들어간다. 이후 도착하는 프로세스는 앞의 프로세스가 작업을 마칠 때까지 대기한다. 프로세스가 작업을 마치면 세마포어는 다음 프로세스에게 임계구역을 사용하라는 동기화 신호를 보낸다. 세마포어는 다음의 연산을 통해 작동된다.
// 내부 동작
// RS = n;
Semaphore(n);
// 내부 동작
// if RS > 0 then RS = RS - 1;
// else block();
P(n);
// 임계구역
// 내부 동작
// RS = RS + 1;
// wake_up();
V(n);
- Semaphore(n): n은 공유 가능한 자원을 나타내며, 전역 변수 RS를 n으로 초기화한다. RS에는 현재 사용 가능한 자원의 수가 저장된다.
- P(): 잠금을 수행한다. RS가 0보다 크면 1만큼 감소시키고, RS가 0보다 작거나 같으면, 0보다 커질 때까지 기다린다.
- V(): 잠금 해제와 동기화를 같이 수행한다. RS 값을 1만큼 증가시키고 세마포어에서 기다리는 프로세스에 임계구역에 진입해도 좋다는 wake_up() 신호를 보낸다
세마포어 알고리즘은 피터슨 알고리즘이나 데커 알고리즘보다 단순하고 사용하기 편하지만, 잘못된 사용으로 인해 임계구역이 보호받지 못할 수 있다. 예를 들면 다음의 경우들이 있다.
- 프로세스가 세마포어를 사용하지 않고 바로 임계구역에 들어간 경우
- P()를 두 번 사용하여 wake_up() 신호가 발생하지 않은 경우 (프로세스가 무한 대기에 빠지게 된다.)
- P()와 V()를 반대로 사용하여 상호 배제가 보장되지 않은 경우
모니터 (Monitor)
모니터(Monitor)는 위의 세마포어 알고리즘의 문제점을 해결한 방식으로, 시스템 호출과 같이 보호할 자원을 임계구역으로 숨기고 임계구역에서 작업할 수 있는 인터페이스만 제공하여 작업을 보호한다. 모니터의 동작 방식은 다음과 같다.
- 임계구역으로 지정된 변수나 자원에 접근하고자 하는 프로세스는 직접 P()나 V()를 사용하지 않고 모니터에 작업 요청을 한다.
- 모니터는 요청받은 작업을 모니터 큐에 저장하고 순서대로 처리하고 그 결과만 프로세스에게 알려준다.
모니터를 사용하게 되면 사용자는 복잡한 코드를 실행하지 않아도 되고, 시스템은 임계구역을 보호할 수 있게 된다.