동기화 문제(Synchronization Problem)란
- 프로세스들의 실행 순서를 정하여 공유 자원의 일관성을 보장하는 것을 동기화(Synchronization)라고 한다.
- 한정적인 시스템 자원에 여러 스레드가 동시에 접근해서 사용하면 문제가 발생할 수 있다.
- 여러 프로세스들이 공유 자원에 동시에 접근하려고 하는 상황을 경쟁 상태(Race Condition)라고 하며, 어떤 프로세스가 마지막으로 데이터에 접근했는지에 따라 데이터의 상태가 달라지게 된다. 즉, 데이터의 일관성을 보장할 수 없어진다.
경쟁 상태(Race Condition)란
다음 세 경우에서 경쟁상태가 발생할 수 있다.
- 커널 모드로 수행 중 인터럽트가 발생하는 경우
- 의도된 동작은 count++과 count--가 모두 반영되어 count가 초기값을 유지하는 것이지만, 만약 Load를 한 후에 인터럽트가 발생하는 경우 인터럽트의 결과는 반영되지 않고 count++만 반영된다.
- 이는 커널 모드의 수행이 끝나기 전에는 인터럽트를 받지 않도록 하는 방법(disable/enable)으로 문제를 해결할 수 있다.
- 프로세스가 시스템 콜을 호출해서 커널 모드로 수행 중인데 Context switch가 발생하는 경우
- 두 프로세스의 주소 공간에서는 데이터를 공유하지 않지만, 시스템 콜을 수행하는 동안에는 둘 다 커널 주소 공간의 데이터를 접근한다.
- 따라서 커널 주소 공간에서 작업을 수행하는 도중에 CPU를 빼앗으면 경쟁 상태가 발생한다.
- 이는 커널 모드를 수행 중일 땐 CPU가 선점되지 않도록 하고, 커널 모드에서 유저 모드로 돌아갈 때 선점되도록 함으로써 해결할 수 있다.
- 멀티 프로세서에서 공유 메모리 내의 커널 데이터에 접근하는 경우
- 어떤 CPU가 마지막으로 Count를 저장했는지에 따라 결과값이 달라진다.
- 싱글 프로세서인 경우 인터럽트 disable/enable 방법으로는 해결할 수 있지만 멀티 프로세서인 경우 인터럽트 제어로는 해결할 수 없다.
- 한 번에 한 CPU만 커널에 들어갈 수 있도록 하는 방법이 있으나 이는 비효율적이다.
만약 두 프로세서가 서로 다른 데이터에 접근하여 Race condition의 가능성이 없음에도 불구하고 한 번에 한 CPU만 커널에 들어갈 수 있기 때문이다.- 따라서, 커널 내부에 있는 각 공유 데이터에 접근할 때마다 그 데이터에 대해서만 lock/unlock을 하는 방식으로 해결할 수 있다.
임계 영역 (Critical Section)
경쟁 상태가 발생할 수 있는 공유 자원이 접근되는 부분을 뜻한다.
만약 어떤 프로세스가 임계영역의 작업을 수행중이라면 다른 프로세스는 해당 임계 영역에 들어와서는 안된다.
문제가 발생하지 않는 임계 영역을 구현하기 위해서는 3가지 조건을 충족해야 한다.
1. 상호배제 (Mutual Exclusion)
- 프로세스 P가 해당 임계 영역을 수행 중이라면, 다른 프로세스들은 해당 임계영역을 수행 해서는 안된다.
- 진행 (Progress)
- 어떠한 프로세스도 임계 영역을 수행하고 있지 않을 때, 임계 영역에 접근하고자 하는 프로세스가 있다면 해당 프로세스를 임계 영역에 들어갈 수 있게 해야한다. 즉, 아무도 임계 영역에 없는데 프로세스가 임계 영역에 못 들어가면 안된다는 뜻이다.
- 한정 대기 (Bounded Waiting)
- 한 프로세스만 계속 임계영역을 수행하는 기아(Starvation)문제를 방지하기 위해 한 번 임계 영역에 들어간 프로세스는 한정(bound)을 두어 다른 프로세스도 해당 임계 영역을 수행 할 수 있도록 해야한다.
공유자원 관리
공유자원 관리를 위해 상호배제를 달성하는 기법이 필요하고 이를 위해 고안된 것이
세마포어, 뮤텍스이다.
- 세마포어와 뮤텍스의 목적은 특정 동기화 대상이 이미 특정 스레드나 프로세스에 의해 사용 중일 경우, 다른 스레드가 해당 동기화 대상에 접근하는 것을 제한 하는 것으로 동일하지만,
관리하는 동기화 대상이 몇 개인지에 따라 차이가 난다.- 두 기법 모두 완벽히 데이터 무결성을 보장할 수 없고 데드락이 발생할 수 있지만 상호배제를 위한 기본적인 방법이다.
- 뮤텍스
- 한 스레드, 프로세스에 의해 소유 될 수 있는 key를 기반으로 한 상호배제 기법이다.
- 1개의 키로 이용하고 나오면 다른 스레드가 가져갈 수 있다.
- 임계 영역에 프로세스가 존재할 때, 다른 프로세스들은 임계 영역에 계속해서 진입하려고 시도하기 때문에 CPU를 낭비하게 된다.
- lock이 반환될 때까지 계속 확인하면서 프로세스가 기다리는 것을 다른 용어로 Busy Waiting이라고 한다.
Busy Waiting은 Critical Section에 진입을 위한 대기 시간이 짧을 때, 즉 Context Switching 하는 비용보다 기다리는 게 더 효율적인 특수한 상황을 위해 고안된 것이다.
단일 CPU 시스템에서는 어차피 Lock을 갖고 있는 스레드를 풀어주기 위해서 Context Switching이 일어나야 하기 때문에 유용하지 않고, 멀티 프로세서 시스템에서 종종 사용된다.
- 세마포어
- 현재 공유자원에 접근 할 수 있는 스레드, 프로세스의 수를 나타내는 값을 두어 상호배제를 달성하는 기법이다.
- Busy Waiting이 필요 없는 동기화 도구이며, 여러 프로세스나 스레드가 임계 영역에 진입할 수 있다.
- 세마포어는 정해진 Counter 갯수만큼의 키를 가지고 여러 스레드가 나눠 쓰게 된다.
- Block & Wakeup 방식을 사용한다.
Block & Wakeup 방식은 임계 영역으로의 진입에 실패한 프로세스를 기다리게 하지 않고 Block 시킨 뒤 임계 영역에 자리가 나면 다시 깨워줌으로써 Busy waiting에서의 CPU 낭비 문제를 해결해준다.
일반적으로 Busy Waiting이 비효율적이지만, 임계 영역이 매우 짧은 경우 Block & Wakeup의 오버헤드가 더 커질 수도 있다.
🙇 참고 사이트 🙇
https://code-lab1.tistory.com/50 https://rebro.kr/176 https://velog.io/@jaeyunn_15/OS-%EB%8F%99%EA%B8%B0%ED%99%94-%EB%AC%B8%EC%A0%9C