Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C/C++ 프로세스 동기화 완벽 정리: 임계 영역부터 세마포어까지

프로세스 동기화(Process Synchronization)는 여러 프로세스가 공유 데이터에 동시에 접근할 때 발생할 수 있는 데이터 불일치(data inconsistency) 문제를 해결하기 위한 기술입니다. 협력 프로세스(cooperating process)란 다른 프로세스에 영향을 주거나 영향을 받는 프로세스를 의미하며, 이러한 관계는 프로세스 간 데이터 불일치를 초래할 수 있습니다. 따라서 데이터의 일관성을 보장하기 위해 프로세스 동기화가 반드시 필요합니다.

임계 영역(Critical Section) 문제

모든 프로세스에는 임계 영역(Critical Section)이라고 불리는 코드 구간이 존재합니다. 이 구간에서 프로세스는 공용 변수 변경, 테이블 갱신, 파일 쓰기 등의 작업을 수행합니다. 임계 영역의 핵심은 한 프로세스가 자신의 임계 영역에서 실행 중일 때, 다른 어떤 프로세스도 자신의 임계 영역에서 실행될 수 없다는 점입니다.

각 프로세스는 임계 영역에 진입하기 전에 반드시 허가를 요청해야 하며, 이 요청을 구현하는 코드 부분을 진입 영역(Entry Section), 코드의 끝부분을 종료 영역(Exit Section), 그리고 나머지 코드를 나머지 영역(Remainder Section)이라고 합니다.

아래는 특정 프로세스 P1의 임계 영역 구조입니다.

C/C++ 프로세스 동기화 완벽 정리: 임계 영역부터 세마포어까지

임계 영역이 충족해야 하는 세 가지 요구 조건

  • 상호 배제(Mutual Exclusion) − 한 프로세스(예: P1)가 자신의 임계 영역에서 실행 중이라면, 다른 프로세스(예: P2)는 자신의 임계 영역에서 실행할 수 없습니다.
  • 진행(Progress) − 임계 영역에서 실행 중인 프로세스가 없고, 진입을 원하는 프로세스들이 있다면, 오직 나머지 영역에서 실행되고 있지 않은 프로세스들만 임계 영역 진입을 요청할 수 있으며, 이 선택이 무한정 연기되어서는 안 됩니다.
  • 한정 대기(Bounded Waiting) − 한 프로세스가 임계 영역 진입을 요청한 후 그 요청이 승인되기까지, 다른 프로세스들이 임계 영역에 진입할 수 있는 횟수에 제한(bounds)이 있어야 합니다.

운영체제의 두 가지 커널 방식

운영체제에서 임계 영역 문제를 처리하는 데 일반적으로 사용되는 두 가지 방식은 다음과 같습니다.

  • 선점형 커널(Preemptive Kernel) − 프로세스가 커널 모드에서 실행 중일 때도 선점(preemption)이 가능합니다.
  • 비선점형 커널(Non-Preemptive Kernel) − 커널 모드에서 실행 중인 프로세스는 선점될 수 없습니다.

피터슨 솔루션(Peterson's Solution)

피터슨 솔루션은 임계 영역 문제에 대한 고전적인 소프트웨어 기반 해법입니다. 이 방식은 임계 영역과 나머지 영역 사이를 번갈아 실행하는 두 개의 프로세스로 제한됩니다. 피터슨 솔루션은 두 프로세스 간에 공유되는 두 개의 데이터 항목을 필요로 합니다.

  • int turn;
  • boolean flag[2];

여기서 변수 turn은 누가 임계 영역에 진입할 차례인지를 나타내고, 배열 flag는 해당 프로세스가 임계 영역에 진입할 준비가 되었는지를 나타냅니다.

  • turn == i이면, 프로세스 Pi가 임계 영역에 진입할 수 있음을 의미합니다.
  • flag[j]가 TRUE이면, 프로세스 j가 임계 영역에 진입할 준비가 되었음을 의미합니다.

아래는 피터슨 솔루션에서 프로세스 Pi의 구조입니다.

C/C++ 프로세스 동기화 완벽 정리: 임계 영역부터 세마포어까지

피터슨 솔루션은 앞서 언급한 세 가지 조건을 모두 만족합니다.

  • 상호 배제 − 한 번에 하나의 프로세스만 임계 영역에 접근할 수 있습니다.
  • 진행 − 임계 영역 밖에 있는 프로세스가 다른 프로세스의 임계 영역 진입을 막지 않습니다.
  • 한정 대기 − 모든 프로세스가 무한정 기다리지 않고 임계 영역에 진입할 기회를 얻습니다.

동기화 하드웨어(Synchronization Hardware)

동기화 하드웨어는 두 가지 유형의 명령어로 구현됩니다.

  • TestAndSet()
  • Swap()

TestAndSet()은 동기화 문제에 대한 하드웨어적 해결책입니다. 이 방식에서는 여러 프로세스가 공유하는 락(Lock) 변수가 사용되며, 이 값은 0 또는 1을 가질 수 있습니다. 1은 락 획득(lock acquired)을, 0은 락 해제(lock released)를 의미합니다.

프로세스가 임계 영역에 진입하려고 할 때는 먼저 락 값을 확인해야 합니다. 락 값이 1이라면, 값이 0으로 변경될 때까지 기다려야 합니다.

아래는 TestAndSet()을 사용한 상호 배제 구현입니다.

C/C++ 프로세스 동기화 완벽 정리: 임계 영역부터 세마포어까지

세마포어(Semaphore)

세마포어는 TestAndSet() 및 Swap() 명령어가 발생시키는 문제들을 극복하기 위해 사용되는 동기화 도구입니다. 세마포어 S는 wait()signal()이라는 두 가지 표준 원자적(atomic) 연산을 통해서만 접근할 수 있는 정수 변수입니다.

wait() 함수:

wait(S) {
   while S <= 0
   ; // 아무 작업도 하지 않음 (busy wait)
   S--;
}

signal() 함수:

signal(S) {
   S++;
}

한 프로세스가 세마포어 값을 수정하는 동안에는, 다른 어떤 프로세스도 동시에 같은 세마포어 값을 조작할 수 없습니다.

아래는 세마포어를 사용한 상호 배제 구현입니다.

C/C++ 프로세스 동기화 완벽 정리: 임계 영역부터 세마포어까지

세마포어의 두 가지 유형

운영체제에서 사용되는 세마포어는 크게 두 가지입니다.

  • 카운팅 세마포어(Counting Semaphore) − 값이 제한 없는(unrestricted) 범위를 가질 수 있는 세마포어입니다. 주로 특정 자원을 사용할 수 있는 프로세스의 수를 제어하는 데 활용됩니다.
  • 이진 세마포어(Binary Semaphore) − 값이 0과 1 사이만 가질 수 있는 세마포어입니다. 뮤텍스 락(Mutex Lock)이라고도 불리며, 운영체제는 이를 통해 여러 프로세스 환경에서 발생하는 임계 영역 문제를 해결합니다.