Race Condition ?
Race Condition이란 두 개 이상의 프로세스가 공통 자원을 병행적으로(Concurrently) 읽거나 쓰는 동작을 할 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다. Race의 뜻 그대로 간단히 말하면 경쟁하는 상태, 즉 두 개의 스레드가 하나의 자원을 놓고 서로 사용하려고 경쟁하는 상황을 이야기한다.
경쟁 프로세스의 경우, 세 가지 제어 문제에 직면한다. Mutual exclusion , Deadlock , Starvation 이다.
📌 Mutual Exclusion
Race condition을 막기 위해서는 두 개 이상의 프로세스가 공용 데이터에 동시에 접근을 하는 것을 막아야 한다.
즉, 한 프로세스가 공용 데이터를 사용하고 있으면 그 자원을 사용하지 못하도록 막거나, 다른 프로세스가 그 자원을 사용하지 못하도록 막으면 이 문제를 피할 수 있다. 이것을 상호 배제(mutual exclusion)라고 부른다.
📌 Deadlock
위와 같은 상호 배제를 시행하면 추가적인 제어 문제가 발생하는데 하나는 교착상태로 Deadlock을 뜻한다. 프로세스가 각자 프로그램을 실행하기 위해 두 자원 모두 엑세스 해야 한다고 가정할 때 프로세스는 두 자원 모두를 필요로 하므로 필요한 두 리소스를 사용하여 프로그램을 수행할 때까지 이미 소유한 리소스를 해제하지 않는다. 이러한 상황에서 두 프로세스는 교착 상태에 빠지게 되는 문제가 발생할 수 있다.
📌 Starvation
이 제어 문제는 "기아 상태"라고도 한다. 이러한 문제는 프로세스들이 더 이상 진행을 하지 못하고 영구적으로 블록되어 있는 상태로, 시스템 자원에 대한 경쟁 도중에 발생할 수 있고 프로세스 간의 통신 과정에도 발생할 수 있는 문제이다. 두 개 이상의 작업이 서로 상대방의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로는 아무것도 완료되지 못하는 상태가 되게 된다.
이렇게 race condition 인 경우 스레드의 실행 순서를 잘 조절하지 않으면 비정상적인 상태가 나오게 되는데 이 문제는 항상 발생하는 것이 아니라 특정 순서대로 수행 되었을 때 발생하는 것이다.
이러한 문제가 발생하지 않도록, OS는 다른 프로세스의 의도하지 않은 간섭으로부터 각 프로세스의 데이터 및 물리적 자원을 보호해야 하며 여기에 메모리, 파일 및 I/O 장치와 관련된 내용이 포함된다. 그리고 프로세스에서 수행하는 내용과 프로세스가 생성하는 결과는 , 다른 동시 프로세스의 실행 속도와 무관 즉 , 기능과 결과는 서로 독립적이어야 한다.
예방 방법
이러한 race condition을 예방할 수 있는 방법으로 Semaphore(세마포어)와 Mutex(뮤텍스)가 있다.
Semaphore (세마포어)
공유된 자원의 데이터를 여러 프로세스나 스레드가 접근하는 것을 막는 것이다. 또한 세마포어는 리소스의 상태를 나타내는 간단한 카운터라고 할 수 있는데, 일반적으로 비교적 긴 시간을 확보하는 리소스에 대해 이용하게 되며, 운영체제의 리소스를 경쟁적으로 사용하는 다중 프로세스에서 행동을 조정하거나 동기화 시키는 기술이다.
다시 말해, 하나의 스레드만 들어가게 할 수도 있고 여러 개의 스레드가 들어가게 할 수 있다. 이것이 뮤텍스와 차이이다.
예제를 통해 살펴보자.
세마포어는 손님이 화장실을 좀 더 쉽게 이용할 수 있는 레스토랑으로 세마포어를 이용하는 레스토랑의 화장실에는 여러 개의 칸이 있고 화장실 입구에는 현재 화장실의 빈 칸 개수를 보여주는 전광판이 있다고 하자.
만약 화장실에 가고 싶다면 입구에서 빈 칸 개수를 확인하고 빈 칸이 1개 이상이라면 빈 칸의 개수를 하나 뺀 다음 화장실로 입장해야 하며 나올 때 빈 칸의 개수를 하나 더해준다.
모든 칸에 사람이 있을 경우 빈 칸의 개수는 0이 되며 이 때 화장실에 들어가고자 하는 사람이 있다면 빈 칸의 개수가 1 혹은 양수로 바뀔 때까지 기다려야 한다.
이처럼 세마포어는 공통으로 관리하는 하나의 값을 이용해 상호배제를 달성한다. 세마포어도 아까와 똑같이 화장실이 공유자원이며 사람들이 스레드, 프로세스이다. 그리고 화장실 빈칸의 개수는 현재 공유자원에 접근할수 있는 스레드, 프로세스의 개수를 나타낸다.
Mutex (뮤텍스)
공유된 자원의 데이터를 여러 스레드가 접근하는 것을 막는 방법이다. 즉, Critical Section(각 프로세스에서 공유 데이터를 엑세스하는 코드 부분)을 가진 스레드들의 Running Time이 서로 겹치지 않게 각각 단독으로 실행되게 하는 기술이다. 다중 프로세스들이 공유 리소스에 대한 접근을 조율하기 위해 locking 과 unlocking 을 사용하는데, 다시 말해 상호 배제를 함으로써 두 스레드가 동시에 사용할 수 없다는 뜻이다.
예제를 통해 살펴보자.
뮤텍스는 화장실이 하나 밖에 없는 식당으로 비유할 수 있다. 화장실을 가기 위해 카운터에서 열쇠를 받아 가야 하며 화장실을 가려고 하는데 카운터에 키가 있으면 화장실에 사람이 없다는 뜻이고 그 열쇠를 이용해 화장실에 들어갈 수 있다.
카운터에 열쇠가 없기 때문에 화장실에 사람이 있다는 뜻이며 화장실을 사용할 수 없다. 여자가 나올 때 까지 기다려야 한다.
곧이어 다른 사람도 화장실에 가려고 카운터에 대기하고 있는데, 앞사람이 화장실에서 나와 카운터에 키를 돌려놓았다. 이제 기다리단 사람들 중 가장 맨 앞에 있던 사람이 키를 받아 화장실에 갈 수 있다.
이것이 뮤텍스가 동작하는 방식이다. 화장실을 이용하는 사람은 프로세스 혹은 스레드이며 화장실은 공유자원, 화장실 키는 공유자원에 접근하기 위해 필요한 어떤 오브젝트이다. 즉 , 뮤텍스는 Key에 해당하는 어떤 오브젝트가 있으며 이 오브젝트를 소유한 (스레드, 프로세스) 만이 공유자원에 접근 가능하다.
세마포어 vs 뮤텍스 차이
- Mutex는 동기화 대상이 오직 1개일 때 사용하며, Semaphore는 동기화 대상이 1개 이상일 때 사용
- Mutex는 자원을 소유할 수 있고, 책임을 가지는 반면 Semaphore는 자원 소유가 불가능
- Mutex는 상태가 0, 1뿐이므로 Lock을 가질 수 있고 소유하고 있는 스레드만이 이 Mutex를 해제할 수 있다.
- Semaphore는 Semaphore를 소유하지 않는 스레드가 Semaphore를 해제할 수 있다.
- Semaphore는 시스템 범위에 걸쳐 있고 파일 시스템 상의 파일로 존재한다. 반면 Mutex는 프로세스의 범위를 가지며 프로세스가 종료될 때 자동으로 Clean up 된다.
참고 자료
https://heeonii.tistory.com/14
https://iredays.tistory.com/125