(참고: Fastcampus 강의, 강민철의 인공지능 시대 필수 컴퓨터 공학 지식)
4. 동기화와 교착 상태
Contents
- 프로세스 동기화
- 생산자와 소비자 문제
- 동기화 해결의 3가지 원칙
- 조건 변수와 모니터
- 교착 상태와 해결 방법
(1) 프로세스 동기화
a) 동기화란?
OS가 제공하는 동기화 (synchronization) 기능?
- 여러 프로세스나 스레드가 동시에 실행될 때 발생할 수 있는 충돌이나 예기치 않은 동작을 방지하기 위한 장치
동기화의 핵심 기능 두 가지
- (1) 실행 순서 제어 (Order of Execution Control)
- (2) 상호 배제 (Mutual Exclusion, 줄여서 Mutex)
개념 | 설명 | 예시 |
---|---|---|
실행 순서 제어 | 작업이 특정 순서대로 실행되도록 제어 | 쓰고 나서 읽기 |
상호 배제 | 동시에 접근하지 못하도록 차단 | 계좌 업데이트 |
b) 공유 자원 & 임계 구역
- 공유 자원: 공동의 자원
- 임계 구역: 동시에 접근 시 문제가 발생할 수 있는 공유 자원에 접근하는 코드
// 공유 자원 접근 코드
lock(); // 진입 전: 잠금
shared_counter++; // 🔴 임계 구역 (공유 자원 접근)
unlock(); // 나올 때: 잠금 해제
(2) 생산자와 소비자 문제
동기화 X시 발생할 수 있는 문제
- Producer: 생산을 하는 프로세스(혹은 스레드)
- Consumer: 소비를 하는 프로세스(혹은 스레드)
a) 예시
- 공유 자원: 버퍼(큐 같은 공간)
- 생산자 (Producer): 데이터를 만들어 버퍼에 넣음
- 소비자 (Consumer): 버퍼에서 데이터를 꺼내 사용함
문제:
- 버퍼가 꽉 찼을 때, 생산자는 기다려야 함
- 버퍼가 비었을 때, 소비자는 기다려야 함
- 동시에 여러 스레드가 접근하면 데이터 손실 or 꼬임 현상이 발생할 수 있음
해결에 필요한 동기화
- Mutex (상호 배제): 생산자와 소비자가 동시에 버퍼를 수정하지 못하게 함
- 조건 변수 (Condition Variable):
- 버퍼가 비었으면 소비자는 대기
- 버퍼가 가득 찼으면 생산자는 대기
b) 요약
구성 요소 | 역할 |
---|---|
공유 자원 | 버퍼 (공통으로 접근하는 공간) |
임계 구역 | 버퍼에 넣고 빼는 코드 |
상호 배제 | 생산자와 소비자가 동시에 버퍼를 수정 못하게 함 |
조건 동기화 | 버퍼가 비거나 가득 찼을 때 대기하도록 제어 |
(3) 동기화 해결의 3가지 원칙
a) 3가지 원칙
- 상호 배제
- 한 프로세스가 임계 구역에 진입했다면, “다른 프로세스는 대기해야 함”
- 진행
- 어떤 프로세스도 임계 구역에 진입하지 않았다면, “진입이 가능해야 함”
- 유한 대기
- 한 프로세스가 임계 구역 진입을 위해 대기하고 있다면, “언젠간 진입이 가능해야 함”
b) 뮤텍스 (Mutex)
- 하나의 스레드만 접근 가능하도록 잠그는(lock) 방식
- 특징
- 공유 자원에 대한 배타적 접근을 보장함
- 한 번에 하나의 스레드만 뮤텍스를 소유할 수 있음
-
예시: 임계 구역(critical section) 보호
- 비유: “열쇠를 하나만 가진 방”
c) 세마포 (Semaphore)
- 동시에 접근 가능한 스레드 수를 제한하는 동기화 기법
- 특징
- 정수 값(count)을 가지며, 그 값으로 접근 가능한 자원의 개수를 나타냄
- 뮤텍스는 세마포어의 특별한 경우 (세마포어 값이 1인 경우)
- 비유: “정해진 수만큼 입장 가능한 방”
항목 | 뮤텍스 | 세마포어 |
---|---|---|
접근 가능 수 | 1 (오직 하나) | N개 (설정 가능) |
소유 개념 | 있음 (소유한 스레드만 해제 가능) | 없음 (누구나 증가/감소 가능) |
목적 | 배타적 자원 접근 | 자원 수 제한 동기화 |
(4) 조건 변수와 모니터
기존 동기화 도구의 문제점:
- 세마포 사용이 편리 X
모니터 = 사용이 간편한 도구!
- 공유 자원에 직접 접근 못 하고, 모니터가 제공하는 함수(인터페이스)를 통해서만 접근함
- 이 함수들은 “자동으로” 잠금(lock)을 걸고, 끝나면 잠금 해제(unlock)
- 즉, 프로그래머가 직접 lock/unlock을 신경 쓰지 않아도 됨 → 사용이 간편함!
조건 변수
- 모니터에서, 실행 순서를 제어하기 위한 동기화를 위해 사용
- 프로세스 상태 전이가 가능한 특별한 변수
- (1)
wait()
: 호출한 프로세스를 “대기 상태”로 전환 - (2)
signal()
: 호출한 프로세스를 “깨움”
- (1)
(5) 교착 상태와 해결 방법
프로세스는 실행을 위해 자원이 필요!
\(\rightarrow\) Q) 그런데 서로의 자원을 무한히 기다리기만 한다면? = “교착 상태”
a) 교착 상태 발생 조건
- 상호 배제: 동시에 자원 사용이 불가능한 경우
- 점유와 대기: 자원을 할당 받은 채, 다른 자원의 할당을 기다리는 경우
- 비선점: 강제로 자원을 뺏을 수 없는 경우
- 원형 대기: 자원을 원형으로 대기할 경우
\(\rightarrow\) 위의 4가지가 모두 있으면, 교착상태가 발생할 수도 있다!
b) 교착 상태 해결 방법
- 교착 상태 “예방”
- 교착 상태 “회피”
- 교착 상태 “검출 후 회복”
b-1) 교착 상태 “예방”
( = 발생 조건 4가지 중 하나를 없애기 )
\(\rightarrow\) But 여러 부작용이 따름
- (1) 상호 배제 조건 없애기
- 조건 해제 방법: 자원을 공유하도록 함
- 한계점: 모든 자원에 대해 적용 불가
- (2) 점유와 대기 조건 없애기
- 조건 해제 방법: 특정 프로세스에 자원을 All or None 할당
- 한계점: 자원의 활용률 저하
- (3) 비선점 조건 없애기
- 선점하여 사용 가능한 자원에 대해서는 효과적 (e.g., CPU)
- 한계점: 모든 자원에 대해 적용 불가 (e.g., 프린터기)
- (4) 원형 대기 조건 없애기
- 조건 해제 방법: 자원에 번호 매기기
- e.g., 오름차순으로 자원 할당
- 조건 해제 방법: 자원에 번호 매기기
b-2) 교착 상태 “회피”
- 교착 상태의 원인 = “무분별한 자원 할당”으로 간주
- 해결책: 교착 상태가 발생하지 않을 정도로만, 조금씩만 자원을 할당
- 자원을 할당하기 전에, 시스템이 안전한 상태(safe state)로 유지될 수 있는지 미리 검사함
- 즉, “이 자원을 지금 주면 나중에 교착 상태에 빠질 가능성이 있나?”를 항상 따져보고, 위험하면 할당 안 함
- Ex) 은행원 알고리즘
은행원 알고리즘 (Banker’s Algorithm)
- 교착상태 회피를 위한 대표적인 알고리즘
- 이름의 유래: 은행원이 대출해줄 때, 모든 고객이 언젠가 대출한 금액을 돌려줄 수 있을지 확인하고 대출함
- 핵심 아이디어:
- (1) 각 프로세스의 최대 자원 요구량을 미리 알아야 함
- (2) 자원을 할당하기 전에, 이 요청을 처리해도 시스템이 안전한 상태인지 검사함
- (3) 안전하다면 자원 할당, 아니면 거절
b-3) 교착 상태 “검출 후 회복”
교착 상태가 발생 시, “그때 가서 회복하자”
- 선점을 통한 회복
- 프로세스 강제 종료를 통한 회복