공용자원이란?

  • 공유자원(Shared resource)은 시스템 안에서 각 프로세스, 스레드가 함께 접근할 수 있는 모니터, 프린트, 메모리, 파일, 데이터 등의 자원이나 변수 등을 의미합니다.

Race Condition 이란?

  • 두 개 이상의 프로세스(혹은 스레드)가 공통 자원을 병행적으로(concurrently) 읽거나 쓰는 동작을 할 때, 공용 자원에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다. Race의 뜻 그대로, 간단히 말하면 경쟁하는 상태
  • 즉 두 개의 프로세스(혹은 스레드)가 하나의 자원을 놓고 서로 사용하려고 경쟁하는 상황을 말한다.
  • 경쟁 상태 시나리오
    1. 두 프로새스
    2. 두 프로세스 P1, P2가 존재합니다.
    3. 두 프로세스의 수행 시간은 동일합니다.
    4. 두 프로세스의 연산은 각기 다릅니다.
    5. 동일한 공유 데이터 값을 읽어 각자 다른 연산을 수행하여 이를 저장합니다.
    • P1 프로세스가 먼저 수행한 경우에는 -100이 저장됩니다.
    • P2 프로세스가 먼저 수행한 경우에는 +100이 저장됩니다.

RaceCondition1.jpg

결과값이 0이어야 하지만 결과는 다르게 출력!

Race Condition이 발생하는 이유

  1. 커널 작업을 수행하는 중에 인터럽트 발생
    • 문제점 : 커널모드에서 데이터를 로드하여 작업을 수행하다가 인터럽트가 발생하여 같은 데이터를 조작하는 경우 → 커널이 가진 전역변수는 모든 프로세스의 공유물 이므로 경쟁상태의 가능성이 있다.
    • 해결법 : 커널모드에서 작업을 수행하는 동안, 인터럽트를 disable 시켜 CPU 제어권을 가져가지 못하도록 한다.
  2. 프로세스가 ‘System Call’을 하여 커널 모드로 진입하여 작업을 수행하는 도중 문맥 교환이 발생할 때
    • 문제점 : 프로세스1이 커널모드에서 데이터를 조작하는 도중, 시간이 초과되어 CPU 제어권이 프로세스2로 넘어가 같은 데이터를 조작하는 경우 ( 프로세스2가 작업에 반영되지 않음 )
    • 해결법 : 프로세스가 커널모드에서 작업을 하는 경우 시간이 초과되어도 CPU 제어권이 다른 프로세스에게 넘어가지 않도록 함
  3. 멀티 프로세서 환경에서 공유 메모리 내의 커널 데이터에 접근할 때
    • 문제점 : 멀티 프로세서 환경에서 2개의 CPU가 동시에 커널 내부의 공유 데이터에 접근하여 조작하는 경우
    • 해결법 : 커널 내부에 있는 각 공유 데이터에 접근할 때마다, 그 데이터에 대한 lock/unlock을 하는 방법 → 즉 세마포어 / 뮤텍스 / 모니터

즉, 근본적인 해결방안은 프로세스(or 스레드)의 순차적 실행을 보장해준다! → 동기화

모니터란? → 수정

  • 기본적으로 뮤텍스, 세마포어, 모니터는 상보 배제, 한정 대기, 융통성이란 조건을 만족합니다.
    • 상호 배제 : 한 프로세스가 임계 영역에 들어갔을 때 다른 프로세스는 들어갈 수 없다.
    • 한정 대기 : 특정 프로세스가 영원히 임계영역에 들어가지 못하면 안 된다.
    • 융통성 : 한 프로세스가 다른 프로세스의 일을 방해해서는 안 된다.
  • 모니터는 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고(캡슐화) 해당 접근에 대해 인터페이스(프로시저)만 제공합니다.
  • 모니터는 모니터 큐를 통해 공유 자원에 대한 작업들을 순차적으로 처리합니다.
  • 모니터는 세마포어보다 구현하기 쉬우며 모니터에서 상호 배제는 자동인 반면에, 세마포에서는 상호 배제를 명시적으로 구현해야하는 차이점이 있습니다.
  • 예시로 Java의 경우 synchronized 키워드가 있습니다.

현대의 운영체제는 임계영역 동기화(상호배제 방법)으로 세마포, 뮤텍스보다는 모니터를 많이 사용합니다. 모니터는 2개의 큐(상호베타 큐, 조건동기 큐)로 구성되어집니다.

  • 상호배타 큐는 말그대로 공유 자원에 하나의 프로세스만 진입하도록 하기 위한 큐입니다.
  • 조건동기 큐는 이미 공유자원을 사용하고 있는 프로세스의 wait() 호출을 통해 조건동기 큐로 들어갈 수 있습니다.

그림을 보면 이해가 쉽습니다.

  1. 왼쪽 큐에 자원이 필요한 쓰레드들을 담아, 한개씩 임계영역 메모리를 사용하게 해줍니다.
  2. 임계영역에 접근하였으나 I/O등으로 아직 데이터가 준비되지 않아 해당 쓰레드가 기다려야 하는 경우 wait()으로 오른쪽 큐에 들어갈 수 있습니다. 이는 쓰레드를 종료하는게 아니라 잠시 기다리게(block)하는 개념입니다.
  3. 오른쪽 큐(조건동기)에 들어가있는 다른 쓰레드에서 notify()로 깨워줄 수 있습니다. 다만 이미 임계영역을 사용중이거나 앞에 다른 쓰레드가 대기중이라면 notify()를 호출 했다해서 바로 실행되는건 아닙니다.

RaceCondition2.png

Race Condition 경우, 발생하는 문제점

경쟁 프로세스의 경우, 세 가지 제어 문제에 직면한다. Mutual exclusion, deadlock, starvation이다.

Mutual exclusion(상호배제)

Deadlock(데드락)

Starvation(기아상태)

이 제어 문제는 ‘기아 상태’라고도 한다. 이러한 문제는 프로세스들이 더 이상 진행을 하지 못하고 영구적으로 블록되어 있는 상태로, 시스템 자원에 대한 경쟁 도중에 발생할 수 있고 프로세스 간의 통신 과정에도 발생할 수 있는 문제이다. 두 개 이상의 작업이 서로 상대방의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로는 아무것도 완료되지 못하는 상태가 되게 된다.

이렇게 race condition 인 경우에는 스레드의 실행 순서를 잘 조절해주지 않으면 이상한 상태, 비정상적인 상태가 나오게 된다. 이 문제는 항상 발생하는 것이 아니라 특정한 순서대로 수행되었을 때 발생하는 것이다. 이 문제는 디버깅을 할 때에는 전혀 보이지 않는 문제점이고, 발생 시에 모든 프로세스에 원하는 결과가 발생하는 것을 보장할 수 없으므로 후에 더욱 큰 문제를 야기할 수 있으므로 반드시 피해야 하는 상황이다.

이러한 문제가 발생하지 않도록, OS는 다른 프로세스의 의도하지 않은 간섭으로부터 각 프로세스의 데이터 및 물리적 자원을 보호해야 하며 여기에 메모리, 파일 및 I/O 장치와 관련된 내용이 포함된다.

그리고 프로세스에서 수행하는 내용과 프로세스가 생성하는 결과는, 다른 동시 프로세스의 실행 속도와 무관, 즉 기능과 결과는 서로 독립적이어야 한다.

해결방안