
Synchronization Primitive
Primitive
- 동기화를 위한 소프트웨어 메커니즘
- 운영체제 단위에서 제공되는 서비스
- Primitive를 이용하여 프로세스 혹은 쓰레드에 대한 동기화를 지원하는 것이 목적
- Atomic Operation, Memory Barriers, Context Switch 등 로우레벨 메커니즘을 이용하여 만들어짐
Primitive의 종류
- mutex, event, condional variables, semaphore 등이 Primitive에 해당됨
- Critical Section 자체는 코드 실행을 위한 경로고 동기화의 대상이기 때문에 Primitive를 의미하지 않음
- 이전에 제시된 동기화를 위한 해결 방법 대신 mutex, semaphore와 같은 제시된 Primitive를 이용하여 Critical Section을 보호하면 됨
- monitor 기법은 조금 더 높은 레벨의 동기화 도구지만, 상황에 따라 Primitive로 취급되기도 함
- Java의 synchronized와 DB의 transaction 등이 monitor의 대표적 사례
Primitive의 동작
- 대부분의 Primitive들은 Lock을 취득하지 못한 프로세스 혹은 쓰레드에 대해 Busy Waiting 대신 Sleep으로 만들어 Sleep Queue에 보관함
- 이후 Context Switch가 발생하며 다른 프로세스 혹은 쓰레드를 처리하게 되므로 Busy Waiting을 통해 낭비되는 CPU 자원을 효율적으로 사용할 수 있게 됨
- Busy Waiting이 무조건 나쁜 것은 아님
- 아주 짧은 시간만 Busy Waiting을 하는 것이 보장된 Critical Section이라면 Context Switch를 하는 것보다는 Busy Waiting이 더 효율적일 수 있음
- Lock을 취득하지 못하여 Primitive의 Sleep Queue에 있는 프로세스들은 언제 깨어날까?
- Lock을 취득했던 프로세스 혹은 쓰레드가 Lock을 해제
- Primitive의 Sleep Queue에 있던 모든 작업들을 모두 깨우면서 스케줄링 됨
- 만약 Lock을 취득했던 프로세스가 직접 하나의 작업을 골라 깨운다면, 대상이 오래 기다렸더라도 Low Priority일 수도 있음
- 스케줄러가 처리하도록 그냥 모든 작업들을 깨워 적절히 스케줄링 되도록 하는 것
Mutex
- 뮤텍스의 범위는 프로세스 내로 한정되어 쓰레드를 대상으로 사용됨
- 이때 사용되는 프로세스들은 동일 프로세스 내의 쓰레드일 수도, Cooperative Process와 같이 다른 프로세스의 쓰레드일 수도 있음
- 뮤텍스에서 Lock을 취득하는 행위를 lock, Lock을 해제하는 행위를 unlock이라고 함
- 오직 Lock을 가지고 있는 쓰레드만이 Lock을 해제할 수 있음
Semaphore
- 세마포어의 범위 자체는 시스템에 걸쳐 있기 때문에 파일 시스템 상의 파일로써 존재함
- 세마포어는 프로세스와 쓰레드를 모두 대상으로 사용할 수 있음
- 세마포어는 크게 Binary Semaphore와 Counting Semaphore로 나눔
- Lock의 수가 단일인지 여럿인지에 따라 나뉨
- Binary Semaphore는 Lock의 수가 단일이므로 mutex처럼 사용할 수 있음
- 세마포어에서 Lock을 취득하는 행위를 wait, lock을 해제하는 행위를 signal이라고 함
- 세마포어에서는 Lock이 프로세스 혹은 쓰레드에 귀속되어 동작하지 않음
- Lock을 일종의 공유자원으로 사용함
- Lock을 취득하지 않은 프로세스 혹은 쓰레드도 Lock을 해제시킬 수 있음
- wait은 P, signal은 V라고 함
- P는 Lock의 수를 감소시키는 연산
- V는 Lock의 수를 증가시키는 연산
- P와 V 자체는 Instruction Solution에 기반
- 호출하여 동작시키는 부분은 시스템 콜로써 이뤄짐
