이번 포스팅에는 프로세스간 동기화와 동기화시 발생할 수 있는 여러 문제와 해결 방법에 대해서 다뤄보도록 하겠습니다.
- 협력하는 프로세스들 사이의 실행 순서 규칙 보장
- 공유되는 데이터 일관성 (consistency) 보장
경쟁 조건 (Race Condition)
공유된 자원의 둘 이상의 입력 또는 조작의 타이밍이나 순서 등이 결과값에 영향을 줄 수 있는 상태를 말합니다.
- 여러 프로세스가 공유 데이터를 동시에 조작할 때, 실행의 특정 순서에 따라 결과가 달라지는 상황
- 공유 데이터 조작을 원자적 연산으로 처리하면 경쟁 조건은 발생하지 않는다.
* 원자적 연산
1. 어떤 연산의 결과가 외부의 간섭에 관계없이 전체가 완료되든지 전혀 실행되지 않은 상태로만 나타남 (All or Nothing)
2. 원자적 연산이 필요한 예
1. 파일 시스템의 메타데이터 갱신
2. 데이터베이스 시스템의 트랜잭션 처리
임계 영역 (Critical Section)
동일한 자원을 동시에 접근하는 작업을 실행하는 코드 영역을 의미합니다.
- 경쟁 조건이 발생할 수 있는 프로그램 코드 부분
- 다른 프로세스와 공유하는 변수나 파일을 변경
- 임계 영역 문제의 해결 방법
- 한 번에 하나의 프로세스만 임계 영역을 실행하도록 보장 (Lock)
- 적절한 동기화 기법을 설계
- 임계 영역 전체를 원자적으로 처리하는 효과
- 한 번에 하나의 프로세스만 임계 영역을 실행하도록 보장 (Lock)
- 임계 영역 문제 해결을 위한
기본 조건
: 3가지 조건을 모두 만족해야지 안전한 동기화를 했다 (=임계 영역 문제를 해결했다)상호 배제 (Mutual Exclusion)
- 어떤 프로세스가 자신의 임계 영역 내에서 실행 중일 때 다른 프로세스는 각자의 임계 영역으로 진입할 수 없다.
진행 (Progress)
- 임계 영역에서 실행 중인 프로세스가 없다면, 임계 영역으로 진입하려는 프로세스들 중 하나는 유한한 시간 내에 진입할 수 있어야 한다.
한정된 대기 (Bounded waiting)
- 한 프로세스가 임계 영역에 대한 진입을 요청한 후에는 다른 프로세스의 임계 영역 진입이 유한한 횟수로 제한되어야 한다.
- 즉 임계 영역에 대한 진입 요청 후 무한히 기다리지 않는다.
동기화의 기본적인 해결 방법
동기화 문제에 대한 가장 기본적인 해결 방법은 Lock 입니다. 특정 프로세스가 공유 자원에 접근 중일때는 다른 프로세스는 접근을 하지 못하도록 Lock을 걸어 해결합니다.
- 임계 영역 진입 부분을 안전하게 처리
- 임계 영역으로의 진입 가능성 확인과 진입을 원자적으로 처리
- 경쟁 조건이 발생하지 않도록 함
- 진입하게 되면 임계 영역을 잠금
- 경쟁 조건이 발생하더라도 안전하게 처리
- 복합적인 진입 조건 검사
- 임계 영역 문제 해결을 위한 요구 조건 만족시킴
- 임계 영역으로의 진입 가능성 확인과 진입을 원자적으로 처리
- Lock 변수에 공동으로 접근하면 똑같이 임계 영역 진입에 대한 문제가 발생 할 수 있습니다.
- 고전적인 소프트웨어 해결책 : 피터슨 알고리즘, 데커 알고리즘 -
CPU가 여러개일때는 동작 안함
- 임계 영역을
Lock
으로 보호 (CPU, 하드웨어 수준
에서Lock
을 건다)Lock
을 획득한 프로세스만 임계 영역으로 진입 허용- 잠겨져 있지 않으면
Lock
을 잠그고 임계 영역에 진입
Lock
을 원자적으로 처리하는 하드웨어 명령어- 중간에 인터럽트 되지 않는 명령어(non-preemptive)
- TestAndSet() : 한 워드의 내용을 확인하고 수정하는 연산을 원자적으로 처리
- Swap() : 두 워드에 내용을 서로 교환하는 연산을 원자적으로 처리
세마포어 (Semaphore)
프로세스
의 동기화 기법으로 공유된 자원의 여러 프로세스가 동시에 접근하는 것을 막는 것
- 기능
- wait(S) : lock 획득
- signal(S) : lock 해제
- 세마포어 구현의 문제
- Busy waiting 문제 - lock의 해제를 기다리는 동안 빈 반복문을 계속 도는걸 반복하기 때문에
Context Switching
이 발생- 프로세스가 Lock을 기다리는 동안 계속 순환(spin) 실행됨
- 짧은 Lock을 기다리는 경우,
Context Switching
비용을 절약할 수 있음 - 일반적으로는 CPU를 점유한 상태로 기다리므로 자원 낭비
- 멀티 프로세서 시스템에 적합한 방법
- 짧은 Lock을 기다리는 경우,
- 해결방법
- block()을 통해서 프로세스를 대기 상태로 전환하고 CPU 스케줄링 실행
- 프로세스가 Lock을 기다리는 동안 계속 순환(spin) 실행됨
- Busy waiting 문제 - lock의 해제를 기다리는 동안 빈 반복문을 계속 도는걸 반복하기 때문에
- 세마포어 자체의 임계 영역 문제
- wait(), signal() 연산의 원자성 보장 문제
- 싱글 프로세서
- wait(), signal() 실행 중에 인터럽트를 금지 (OS 수준에서) -> 인터럽트를 막으면 시스템이 동작하지 않을 수 있음
- 멀티 프로세서
- 모든 프로세서에서 인터럽트를 금지
- 어려운 작업이며 성능 감소를 유발
- 별도의 Lock 기법 필요
뮤텍스 (Mutex)
스레드
의 동기화 기법으로 공유된 자원의 여러 스레드가 동시에 접근하는 것을 막는 것
- 기아 (Starvation) - 무한 대기
- 프로세스가 세마포어 큐에서 무한히 대기하는 상황
- 높은 우선순위의 프로세스가 계속 들어오면 무한히 대기만 하는 상황
- 시간이 지날 수록 우선순위를 높여주는 방법으로 해결 (Aging)
- 우선순위 역전 (Priority Inversion)
- 공유 자원에 대한 허가를 기다리는 동안 낮은 우선순위 프로세스와 스케줄링 순서가 뒤바뀌는 상황
- 해결방법 : 우선순위 상속 프로토콜 -> 상위 우선순위 프로세스를 막고 있는 동안 우선순위를 상속
뮤텍스와 세마포어의 차이
세마포어 | 뮤텍스 | |
---|---|---|
개수 | 여러개 | 하나 |
변화 | 뮤텍스로 변경 가능 | 세마포어로 변경 불가능 |
소유 | X | O |
해제 | 세마포어를 가지지 않은 스레드도 해제 가능 | 뮤텍스를 소유한 스레드만 해제 가능 |
교착상태 (Deadlock)
서로 Lock을 놓기만을 기다리는 상황으로 두 개 이상의 프로세스가 어떤 사건을 기다리고 있는데, 같이 기다리는 프로세스 중 하나만이 그 사건을 발생시킬 수 있는 상황
[데드락이 발생할 수 있는 상태]
아래 4가지 조건을 모두 만족하지 않으면 교착상태가 발생할 수 있습니다.
- 상호 배제 (Mutual exclusion) : 자원은 한 번에 한 프로세스만이 사용할 수 있어야 합니다.
- 점유 대기 (Hold and wait) : 최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용하고 있는 자원을 추가로 점유하기 위해 대기하는 프로세스가 있어야 합니다.
- 비선점 (No preemption) : 다른 프로세스에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없어야 합니다.
- 순환 대기 (Circular wait) : 프로세스의 집합 {P0, P1, ,…Pn}에서 P0는 P1이 점유한 자원을 대기하고 P1은 P2가 점유한 자원을 대기하고 P2…Pn-1은 Pn이 점유한 자원을 대기하며 Pn은 P0가 점유한 자원을 요구해야 합니다.