3 분 소요

프로세스 동기화에 대해서 이해하기 위해서는 우선 임계 영역과 경쟁 상태에 대해서 이해해야 한다. 차례대로 살펴보며 이해해보자.


임계 영역 (Critical Section)

정의

임계 영역(Critical Section)이란 프로세스간에 공유자원을 접근함에 있어서 문제가 발생하지 않도록 한번에 하나의 프로세스만 공유자원에 접근하도록 보장해줘야 하는 영역을 말한다.

특징

상호 배제 (Mutual Exclusion) : 하나의 프로세스가 임계 영역에 들어가 있으면 다른 프로세스는 들어갈 수 없어야 한다.

진행 (Progress) : 임계 영역에 들어간 프로세스가 없는 상태에서 임계 영역에 들어가려는 프로세스가 여러 개라면 어느 프로세스가 먼저 들어갈 지 결정할 수 있어야 한다.

한정 대기 (Bounded Waiting) : 다른 프로세스의 기아를 방지하기 위해, 한번 임계 영역에 들어간 프로세스는 다음 임계 영역에 들어갈 때 제한을 두어야 한다.

경쟁 상태 (Race Condition)

정의

경쟁 상태 (Race Condition)란 공유 자원에 대해 여러 프로세스가 동시에 접근할 때, 결과값이 접근 순서나 접근 시점에 따라 달라질 수 있는 것을 말한다.

경쟁 상태가 발생할 수 있는 상황

커널 작업 수행 중에 인터럽트가 발생하는 경우

해결 방법 : 커널 모드에서 작업을 수행하는 동안 인터럽트를 disable 시켜서 해결할 수 있다.

프로세스가 시스템콜을 호출하여 커널 모드로 진입하여 작업을 수행하는 도중에 문맥교환이 발생한 경우

프로세스가 커널 모드에서 작업하는 경우에는 CPU 허용 시간이 초과되더라도 CPU 제어권을 다른 프로세스로 넘기지 않게 함으로써 해결할 수 있다.

멀티 프로세스 환경에서 공유 메모리 내의 커널 데이터에 접근할 경우

커널 내부의 공유 메모리에 접근할 때마다 Lock/UnLock을 하여 해결할 수 있다.


예시

간단한 예를 살펴보며 이해해 보자.

코드

img1

이 코드에서 counter는 공급자와 소비자가 공유하는 부분이다. 만약 공급자에서 counter++을 하는 시점에 소비자에서 counter--를 동시에 실행하게 되면 어떻게 될까? (여기서의 counter 연산은 원자적이지 않다.)

img1

counter++이라는 연산은 원자적인 연산처럼 보이지만 사실 어셈블러로 내려가면 그렇지 않다. 위의 코드를 보면 알 수 있듯이 3개의 연산으로 쪼개진다. 공급자가 counter++을 호출한 시점에 소비자가 counter--를 호출한 상황을 살펴보자.

img1

register1에는 6이 저장되고 counter에 옮기는 작업을 하는 중간에 counter--가 끼어들어서 counter의 값이 6이 되자마자 register2(4)의 값으로 덮어써서 공급자 입장에서 counter값 6을 기대했지만 4가 나오는 결과가 도출된다.

그렇다면 어떻게 이러한 문제들을 해결할 수 있을까? 이제부터 이에 대해서 알아보자.


뮤텍스 (Mutex)

정의

뮤텍스란 여러 스레드를 실행하는 환경에서 자원에 대한 접근에 제한을 강제하기 위한 동기화 매커니즘이다.

img1

연산

Acquire : 현재의 임계 구역에 들어갈 권한을 가져온다. 만약 다른 프로세스가 임계 구역에 있다면 나올때까지 대기한다.

Release : 현재의 임계 구역을 모두 사용했음을 알린다. Release후에 다른 프로세스가 임계 구역에 접근할 수 있게 된다.

특징

boolean 타입의 lock 변수를 통해 잠금 여부를 확인할 수 있고 한 개의 프로세스/스레드만 공유자원을 소유하고 해제할 수 있다. 그리고 대기중인 프로세스/스레드가 busy waiting 방식으로 대기하지 않고 대기 큐에 잠들어서 기다린다.(CPU 자원 소비 X)

  • busy waiting : CPU의 자원을 지속적으로 사용하며 특정 조건이 만족할 때까지 대기하는 방식 (while 문을 계속 돌고있다고 생각하면 된다.)


세마포어 (Semaphore)

세마포어는 교착 상태에 대한 해법으로 두 개의 원자적 함수로 제어되는 정수 변수로 멀티프로그래밍 환경에서 공유자원에 대한 접근 제어를 하는 방법으로 사용되며, 1개의 공유되는 자원에 제한된 개수의 프로세스, 또는 스레드만 접근할 수 있도록 한다.

img1

흐름

  1. 허용되는 공유 자원에 접근할 수 있는 수로 세마포어 값을 초기화한다.

  2. 프로세스/스레드가 공유 자원에 접근할 때마다 세마포어 변수의 값을 1감소 시킨다. (세마포어 변수가 0이 되면 꽉찬 것이다.)

  3. 세마포어 변수값이 0이하면 더 이상 접근할 수 없다는 의미이고 세마포어 변수에 절대값을 씌운 값이 현재 대기큐에서 잠들고 있는 프로세스/스레드의 수이다

특징

뮤텍스와 다르게 한 개 이상의 프로세스/스레드가 공유 자원에 접근할 수 있다. Wait(P) 연산을 통해 공유자원을 획득하고 Signal(V) 연산을 통해 해제한다. 세마포어도 뮤텍스와 마찬가지로 busy waiting 방식이 아닌 대기 큐에서 잠들다가 깨어나는 방식을 사용한다.

종류

이진 세마포어 : 세마포어 변수값이 0, 1만 될 수 있으며 최대 한개의 프로세스/스레드가 공유 자원에 접근 가능한 세마포어이다.

카운팅 세마포어 : 최대 n개의 프로세스/스레드가 공유 자원에 접근 가능한 세마포어이다.


뮤텍스와 세마포어의 차이점

  1. 뮤텍스는 소유한 프로세스/스레드만 뮤텍스 락을 변경할 수 있다. 반면 세마포어는 여러 프로세스/스레드가 세마포어 변수값을 변경할 수 있다.
  2. 뮤텍스는 세마포어로 사용될 수 없지만 세마포어는 뮤텍스로서 사용될 수 있다.
  3. 세마포어는 실행순서의 동기화를 할 수 있다.


교착상태 (dead lock)

정의

둘 이상의 프로세스가 자원을 점유한 상태에서 서로 다른 프로세스가 점유하고 있는 자원을 요구하며 무한정 기다리는 상황

교착상태 조건

비선점 (Non Preemptive)

다른 프로세스가 사용하고 있는 자원을 선점할 수 없다.

순환 대기 (Circular wait)

두 개 이상의 프로세스가 자원 접근을 기다릴 때, 관계가 순환적 구조를 이룬다.

점유 대기 (Hold and Wait)

공유 자원에 대한 접근 권한을 가진 채로 다른 자원에 대한 접근 권한을 요구한다.

상호 배제 (Mutual Exclusion)

한번에 한 프로세스만 공유 자원에 접근이 가능하며, 접근 권한이 제한적이다.


-> 위 4가지 조건중에 한 가지라도 만족하지 않으면 데드락은 발생하지 않는다.

교착상태 예시

img1

자원 R1을 가진 P1과 자원 R2를 가진 P2가 있다. P1은 R2를 필요로 하고 P2는 R1을 필요로 한다면 두 프로세스는 서로의 자원을 얻기 위해 무한정 기다리는 상황이 발생한다.


교착상태 해결

위에 설명한 4가지 조건중 하나를 만족시키지 않게 상태를 변경하면 된다. 하지만 변경에 여러 비용이 수반된다. 현대의 운영체제는 교착상태가 발생하면 강제 종료를 시켜서 해결한다.


참고