콘텐츠로 이동

제 8장: 교착 상태 (Deadlocks) 🔒

📖 목차 (Table of Contents)

  1. 개요
  2. 시스템 모델
  3. 교착 상태 특성
  4. 자원 할당 그래프
  5. 교착 상태 처리 방법
  6. 교착 상태 예방
  7. 교착 상태 회피
  8. 교착 상태 탐지
  9. 교착 상태로부터의 복구
  10. 핵심 개념 정리
  11. 연습 문제

개요

**교착 상태(Deadlock)**는 둘 이상의 프로세스가 서로 다른 프로세스가 점유한 자원을 무한정 기다리는 상황입니다. 이는 시스템의 성능을 심각하게 저하시키고 전체 시스템을 마비시킬 수 있는 중요한 문제입니다.

graph TD
    A[교착 상태 문제] --> B[발생 조건 분석]
    A --> C[해결 전략 수립]
    A --> D[시스템 성능 최적화]

    B --> B1[상호 배제]
    B --> B2[점유와 대기]
    B --> B3[비선점]
    B --> B4[순환 대기]

    C --> C1[예방<br/>Prevention]
    C --> C2[회피<br/>Avoidance]
    C --> C3[탐지 및 복구<br/>Detection & Recovery]
    C --> C4[무시<br/>Ignore]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style D fill:#e8f5e8

🎯 학습 목표

이 장을 통해 다음을 이해할 수 있습니다: - 교착 상태의 발생 조건과 특성 - 교착 상태 예방, 회피, 탐지 기법 - 은행가 알고리즘의 원리와 적용 - 실제 시스템에서의 교착 상태 해결 방안


시스템 모델

🏗️ 시스템 구성 요소

graph TD
    A[시스템] --> B[자원 유형<br/>Resource Types]
    A --> C[프로세스<br/>Processes]

    B --> B1[R₁: CPU 사이클]
    B --> B2[R₂: 메모리 공간]
    B --> B3[R₃: I/O 장치]
    B --> B4[R₄: 파일]

    C --> C1[요청<br/>Request]
    C --> C2[사용<br/>Use]
    C --> C3[해제<br/>Release]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e8

시스템은 다음과 같이 구성됩니다: - 자원 유형: R₁, R₂, ..., Rₘ (CPU 사이클, 메모리 공간, I/O 장치 등) - 각 자원 유형 Rᵢ: Wᵢ개의 인스턴스 보유 - 프로세스의 자원 사용 패턴: 요청 → 사용 → 해제

🔄 다중 스레드에서의 교착 상태 예시

sequenceDiagram
    participant T1 as 스레드 1
    participant M1 as Mutex 1
    participant M2 as Mutex 2
    participant T2 as 스레드 2

    T1->>M1: lock(mutex1) ✓
    T2->>M2: lock(mutex2) ✓
    T1->>M2: lock(mutex2) ⏳
    T2->>M1: lock(mutex1) ⏳

    Note over T1,T2: 교착 상태 발생!

실제 코드 예시:

pthread_mutex_t first_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t second_mutex = PTHREAD_MUTEX_INITIALIZER;

// 스레드 1
void* thread1(void* arg) {
    pthread_mutex_lock(&first_mutex);
    sleep(1);  // 다른 스레드가 second_mutex를 잡을 시간 제공
    pthread_mutex_lock(&second_mutex);  // 교착 상태!

    // 작업 수행

    pthread_mutex_unlock(&second_mutex);
    pthread_mutex_unlock(&first_mutex);
    return NULL;
}

// 스레드 2
void* thread2(void* arg) {
    pthread_mutex_lock(&second_mutex);
    sleep(1);  // 다른 스레드가 first_mutex를 잡을 시간 제공
    pthread_mutex_lock(&first_mutex);   // 교착 상태!

    // 작업 수행

    pthread_mutex_unlock(&first_mutex);
    pthread_mutex_unlock(&second_mutex);
    return NULL;
}


교착 상태 특성

🔍 교착 상태의 네 가지 필수 조건

교착 상태가 발생하려면 다음 네 가지 조건이 동시에 만족되어야 합니다:

graph TD
    A[교착 상태<br/>4가지 필수 조건] --> B[상호 배제<br/>Mutual Exclusion]
    A --> C[점유와 대기<br/>Hold and Wait]
    A --> D[비선점<br/>No Preemption]
    A --> E[순환 대기<br/>Circular Wait]

    B --> B1["한 번에 하나의 프로세스만<br/>자원을 사용할 수 있음"]
    C --> C1["자원을 보유한 채로<br/>다른 자원을 대기"]
    D --> D1["자원을 강제로<br/>빼앗을 수 없음"]
    E --> E1["프로세스들이 원형으로<br/>서로의 자원을 대기"]

    style A fill:#ffebee
    style B fill:#fff3e0
    style C fill:#e8f5e8
    style D fill:#f3e5f5
    style E fill:#e1f5fe

1. 상호 배제 (Mutual Exclusion)

graph LR
    A[자원 R] --> B[프로세스 P1]
    A -.-> C[프로세스 P2]
    A -.-> D[프로세스 P3]

    B --> B1[사용 중]
    C --> C1[대기 중]
    D --> D1[대기 중]

    style B fill:#e8f5e8
    style C fill:#ffebee
    style D fill:#ffebee

2. 점유와 대기 (Hold and Wait)

graph TD
    A[프로세스 P1] --> B[보유: 자원 R1]
    A --> C[대기: 자원 R2]

    D[프로세스 P2] --> E[보유: 자원 R2]
    D --> F[대기: 자원 R3]

    style B fill:#e8f5e8
    style C fill:#ffebee
    style E fill:#e8f5e8
    style F fill:#ffebee

3. 비선점 (No Preemption)

  • 자원은 해당 프로세스가 자발적으로 해제할 때까지 강제로 회수할 수 없음
  • 프로세스가 작업을 완료한 후에만 자원 해제

4. 순환 대기 (Circular Wait)

graph TD
    A[P0] -->|대기| B[R1]
    B -->|점유| C[P1]
    C -->|대기| D[R2]
    D -->|점유| E[P2]
    E -->|대기| F[R0]
    F -->|점유| A

    style A fill:#e1f5fe
    style C fill:#fff3e0
    style E fill:#f3e5f5

자원 할당 그래프

📊 자원 할당 그래프 구성 요소

graph TD
    A[자원 할당 그래프] --> B[꼭짓점<br/>Vertices]
    A --> C[간선<br/>Edges]

    B --> B1[프로세스 집합 P]
    B --> B2[자원 유형 집합 R]

    C --> C1[요청 간선<br/>Pi → Rj]
    C --> C2[할당 간선<br/>Rj → Pi]

    style A fill:#e1f5fe
    style B1 fill:#fff3e0
    style B2 fill:#e8f5e8
    style C1 fill:#ffebee
    style C2 fill:#f3e5f5

그래프 표기법

graph LR
    A[P1] -->|요청| B[R1]
    B -->|할당| C[P2]

    subgraph "자원 R1"
        D[●]
        E[●]
        F[○]
    end

    A --> A1[프로세스]
    D --> D1[할당된 인스턴스]
    F --> F1[사용 가능한 인스턴스]

🔍 교착 상태 탐지 규칙

graph TD
    A[그래프 분석] --> B[사이클 없음]
    A --> C[사이클 있음]

    B --> B1[교착 상태 없음 ✓]

    C --> C1[자원당 인스턴스 1개]
    C --> C2[자원당 인스턴스 여러 개]

    C1 --> C11[교착 상태 있음 ⚠️]
    C2 --> C21[교착 상태 가능성 있음 ❓]

    style B1 fill:#e8f5e8
    style C11 fill:#ffebee
    style C21 fill:#fff3e0

예시 1: 교착 상태가 있는 경우

graph LR
    P1 -->|요청| R2
    R1 -->|할당| P1
    P2 -->|요청| R1
    R2 -->|할당| P2

    style P1 fill:#e1f5fe
    style P2 fill:#e1f5fe
    style R1 fill:#ffebee
    style R2 fill:#ffebee

예시 2: 사이클이 있지만 교착 상태가 없는 경우

graph LR
    P1 -->|요청| R1
    R1 -->|할당| P2
    P2 -->|요청| R2
    R2 -->|할당| P1
    R2 -->|할당| P3

    style P3 fill:#e8f5e8

교착 상태 처리 방법

🛠️ 네 가지 접근 방식

graph TD
    A[교착 상태 처리 방법] --> B[예방<br/>Prevention]
    A --> C[회피<br/>Avoidance]
    A --> D[탐지 및 복구<br/>Detection & Recovery]
    A --> E[무시<br/>Ignore]

    B --> B1["4가지 조건 중<br/>하나를 무효화"]
    C --> C1["안전한 상태만<br/>유지하도록 제어"]
    D --> D1["교착 상태 발생 허용<br/>후 복구"]
    E --> E1["문제가 발생하지<br/>않는다고 가정"]

    style B fill:#e8f5e8
    style C fill:#fff3e0
    style D fill:#f3e5f5
    style E fill:#ffebee

📊 방법별 비교

방법 장점 단점 적용 환경
예방 • 교착 상태 원천 차단
• 단순한 구현
• 자원 활용률 저하
• 성능 감소
제한적 자원 환경
회피 • 높은 자원 활용률
• 안전성 보장
• 복잡한 알고리즘
• 오버헤드 증가
자원 요구량 예측 가능
탐지/복구 • 평상시 오버헤드 최소
• 유연한 자원 관리
• 복구 비용
• 일시적 성능 저하
교착 상태 빈도 낮음
무시 • 구현 비용 최소
• 성능 최적화
• 시스템 마비 위험
• 예측 불가능
교착 상태 극히 드뭄

교착 상태 예방

🚫 네 가지 조건 무효화

1. 상호 배제 무효화

graph TD
    A[상호 배제 무효화] --> B[공유 가능한 자원]
    A --> C[공유 불가능한 자원]

    B --> B1[읽기 전용 파일]
    B --> B2[불변 데이터]

    C --> C1[프린터]
    C --> C2[쓰기 가능 파일]
    C --> C3[임계 구역]

    style B fill:#e8f5e8
    style C fill:#ffebee

한계: 많은 자원이 본질적으로 공유 불가능하므로 현실적으로 어려움

2. 점유와 대기 무효화

graph TD
    A[점유와 대기 무효화] --> B[모든 자원을<br/>한 번에 요청]
    A --> C[자원을 보유하지 않은<br/>상태에서만 요청]

    B --> B1[장점: 단순함]
    B --> B2[단점: 자원 활용률 저하]

    C --> C1[장점: 유연성]
    C --> C2[단점: 굶주림 가능]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#f3e5f5

방법 1: 프로세스 시작 전 모든 필요 자원 요청

// 의사 코드
request_all_resources();  // 모든 자원 요청
if (all_granted) {
    execute_process();
    release_all_resources();
}

방법 2: 추가 자원 필요 시 보유 자원 모두 해제 후 재요청

// 의사 코드
if (need_additional_resource()) {
    release_all_current_resources();
    request_all_needed_resources();
}

3. 비선점 무효화

sequenceDiagram
    participant P1 as 프로세스 1
    participant R as 자원 관리자
    participant P2 as 프로세스 2

    P1->>R: 자원 A 요청
    R->>P1: 자원 A 할당
    P1->>R: 자원 B 요청 (대기 상태)
    R->>P1: 보유 자원 모두 선점
    Note over P1: 자원 A 반납, 대기 목록에 추가
    P2->>R: 자원 A 요청
    R->>P2: 자원 A 할당 (P1에게서 선점한 것)

적용 가능한 자원: - CPU (컨텍스트 스위치를 통해) - 메모리 (스와핑을 통해)

적용 어려운 자원: - 프린터, 테이프 드라이브 등

4. 순환 대기 무효화

graph TD
    A[순환 대기 무효화] --> B[자원에 순서 부여]

    B --> C["총 순서 정의<br/>F: R → N"]
    C --> D[오름차순 요청 규칙]

    D --> E["예시: F_printer = 1<br/>F_tape = 2<br/>F_disk = 3"]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e8
    style D fill:#f3e5f5

구현 예시:

// 자원 순서 정의
#define MUTEX_A 1
#define MUTEX_B 2
#define MUTEX_C 3

void correct_order() {
    acquire(MUTEX_A);  // 1번 먼저
    acquire(MUTEX_B);  // 2번 다음
    acquire(MUTEX_C);  // 3번 마지막

    // 작업 수행

    release(MUTEX_C);  // 역순으로 해제
    release(MUTEX_B);
    release(MUTEX_A);
}

void incorrect_order() {
    // 이런 순서는 금지!
    acquire(MUTEX_C);  // ❌ 높은 번호 먼저
    acquire(MUTEX_A);  // ❌ 낮은 번호 나중에
}


교착 상태 회피

🛡️ 안전 상태 개념

graph TD
    A[시스템 상태] --> B[안전 상태<br/>Safe State]
    A --> C[불안전 상태<br/>Unsafe State]

    B --> B1[안전 순서 존재]
    B --> B2[교착 상태 없음 보장]

    C --> C1[안전 순서 없음]
    C --> C2[교착 상태 가능성]

    style B fill:#e8f5e8
    style C fill:#ffebee

안전 상태: 모든 프로세스가 완료될 수 있는 실행 순서가 존재하는 상태

안전 순서 (Safe Sequence)

프로세스 순서 이 안전하려면: - 각 Pᵢ에 대해, Pᵢ가 여전히 요청할 수 있는 자원 ≤ 현재 사용 가능한 자원 + 모든 Pⱼ (j < i)가 보유한 자원

🏦 은행가 알고리즘 (Banker's Algorithm)

graph TD
    A[은행가 알고리즘] --> B[데이터 구조]
    A --> C[안전성 알고리즘]
    A --> D[자원 요청 알고리즘]

    B --> B1["Available[m]"]
    B --> B2["Max[n×m]"]
    B --> B3["Allocation[n×m]"]
    B --> B4["Need[n×m]"]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e8
    style D fill:#f3e5f5

데이터 구조

  • n: 프로세스 수
  • m: 자원 유형 수
  • Available[j]: 자원 유형 j의 사용 가능한 인스턴스 수
  • Max[i,j]: 프로세스 i가 자원 유형 j를 최대 몇 개까지 요청할 수 있는지
  • Allocation[i,j]: 프로세스 i에게 현재 할당된 자원 유형 j의 인스턴스 수
  • Need[i,j]: 프로세스 i가 완료를 위해 추가로 필요한 자원 유형 j의 인스턴스 수

관계식: Need[i,j] = Max[i,j] - Allocation[i,j]

안전성 알고리즘

def safety_algorithm():
    # 1. 초기화
    Work = Available.copy()
    Finish = [False] * n

    # 2. 안전 순서 찾기
    safe_sequence = []

    while len(safe_sequence) < n:
        found = False

        for i in range(n):
            if not Finish[i] and Need[i] <= Work:
                # 3. 프로세스 i 완료 시뮬레이션
                Work += Allocation[i]
                Finish[i] = True
                safe_sequence.append(i)
                found = True
                break

        if not found:
            return False, []  # 불안전 상태

    return True, safe_sequence  # 안전 상태

실제 예시

초기 상태:

프로세스 | Max  | Allocation | Need | Available
        | A B C | A B C      | A B C | A B C
--------|------|------------|------|-------
   T0   | 7 5 3 | 0 1 0     | 7 4 3 | 3 3 2
   T1   | 3 2 2 | 2 0 0     | 1 2 2 |
   T2   | 9 0 2 | 3 0 2     | 6 0 0 |
   T3   | 2 2 2 | 2 1 1     | 0 1 1 |
   T4   | 4 3 3 | 0 0 2     | 4 3 1 |

안전성 검사:

graph LR
    A[T1 실행<br/>Need: 1,2,2<br/>Available: 3,3,2] --> B[T3 실행<br/>Need: 0,1,1<br/>Available: 5,3,2]
    B --> C[T4 실행<br/>Need: 4,3,1<br/>Available: 7,4,3]
    C --> D[T2 실행<br/>Need: 6,0,0<br/>Available: 7,4,5]
    D --> E[T0 실행<br/>Need: 7,4,3<br/>Available: 10,4,7]

    style A fill:#e8f5e8
    style B fill:#e8f5e8
    style C fill:#e8f5e8
    style D fill:#e8f5e8
    style E fill:#e8f5e8

안전 순서:


교착 상태 탐지

🔍 단일 인스턴스 자원

대기 그래프 (Wait-for Graph)

graph TD
    A[자원 할당 그래프] --> B[대기 그래프]

    subgraph "자원 할당 그래프"
        P1 --> R1
        R2 --> P1
        P2 --> R2
        R1 --> P2
    end

    subgraph "대기 그래프"
        P1_w --> P2_w
        P2_w --> P1_w
    end

    B --> C["사이클 탐지<br/>O(n^2)"]

    style C fill:#ffebee
  • 노드: 프로세스만 포함
  • 간선: Ti → Tj (Ti가 Tj를 대기)
  • 교착 상태: 사이클 존재 시

📊 다중 인스턴스 자원

탐지 알고리즘

def detection_algorithm():
    # 1. 초기화
    Work = Available.copy()
    Finish = [False] * n

    # 자원을 할당받지 않은 프로세스는 완료로 표시
    for i in range(n):
        if all(Allocation[i][j] == 0 for j in range(m)):
            Finish[i] = True

    # 2. 완료 가능한 프로세스 찾기
    while True:
        found = False

        for i in range(n):
            if not Finish[i] and Request[i] <= Work:
                # 프로세스 i 완료
                Work += Allocation[i]
                Finish[i] = True
                found = True

        if not found:
            break

    # 3. 교착 상태 검사
    deadlocked = [i for i in range(n) if not Finish[i]]
    return len(deadlocked) > 0, deadlocked

탐지 빈도 결정

graph TD
    A[탐지 알고리즘 호출 시점] --> B[자원 요청 시마다]
    A --> C[주기적으로]
    A --> D[CPU 사용률 기반]

    B --> B1[장점: 즉시 탐지]
    B --> B2[단점: 높은 오버헤드]

    C --> C1[장점: 예측 가능한 오버헤드]
    C --> C2[단점: 탐지 지연]

    D --> D1[장점: 적응적]
    D --> D2[단점: 복잡한 구현]

    style B1 fill:#e8f5e8
    style C1 fill:#fff3e0
    style D1 fill:#f3e5f5

교착 상태로부터의 복구

🔧 복구 방법

graph TD
    A[복구 방법] --> B[프로세스 종료<br/>Process Termination]
    A --> C[자원 선점<br/>Resource Preemption]

    B --> B1[모든 교착 프로세스 종료]
    B --> B2[한 번에 하나씩 종료]

    C --> C1[희생자 선택<br/>Victim Selection]
    C --> C2[롤백<br/>Rollback]
    C --> C3[굶주림 방지<br/>Starvation Prevention]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#f3e5f5

프로세스 종료

종료 우선순위 기준
  1. 프로세스 우선순위
  2. 실행 시간 vs 남은 시간
  3. 사용한 자원 vs 필요한 자원
  4. 종료해야 할 프로세스 수
  5. 대화형 vs 배치 처리
def select_victim_process(deadlocked_processes):
    min_cost = float('inf')
    victim = None

    for process in deadlocked_processes:
        cost = calculate_termination_cost(process)
        if cost < min_cost:
            min_cost = cost
            victim = process

    return victim

def calculate_termination_cost(process):
    # 비용 계산 요소들
    priority_cost = (MAX_PRIORITY - process.priority) * 10
    time_cost = process.execution_time
    resource_cost = sum(process.allocated_resources)

    return priority_cost + time_cost + resource_cost

자원 선점

sequenceDiagram
    participant OS as 운영체제
    participant V as 희생자 프로세스
    participant R as 자원
    participant W as 대기 프로세스

    OS->>V: 자원 선점 요청
    V->>R: 자원 해제 (강제)
    R->>W: 자원 할당
    OS->>V: 안전 상태로 롤백
    Note over V: 체크포인트에서 재시작
롤백 전략
  1. 전체 롤백: 프로세스를 처음부터 재시작
  2. 부분 롤백: 교착 상태가 해결될 만큼만 롤백
굶주림 방지
typedef struct {
    int process_id;
    int preemption_count;
    time_t last_preemption;
} PreemptionHistory;

bool should_preempt(PreemptionHistory* history) {
    const int MAX_PREEMPTIONS = 3;
    const time_t MIN_INTERVAL = 60; // 초

    if (history->preemption_count >= MAX_PREEMPTIONS) {
        time_t current_time = time(NULL);
        if (current_time - history->last_preemption < MIN_INTERVAL) {
            return false; // 굶주림 방지
        }
        history->preemption_count = 0; // 카운터 리셋
    }

    return true;
}

핵심 개념 정리

📊 교착 상태 해결 방법 비교

graph TD
    A[교착 상태 해결 방법] --> B[예방<br/>Prevention]
    A --> C[회피<br/>Avoidance]  
    A --> D[탐지/복구<br/>Detection/Recovery]
    A --> E[무시<br/>Ignore]

    B --> B1["💰 비용: 높음<br/>🛡️ 안전성: 최고<br/>⚡ 성능: 낮음"]
    C --> C1["💰 비용: 중간<br/>🛡️ 안전성: 높음<br/>⚡ 성능: 중간"]
    D --> D1["💰 비용: 낮음-중간<br/>🛡️ 안전성: 중간<br/>⚡ 성능: 높음"]
    E --> E1["💰 비용: 최저<br/>🛡️ 안전성: 없음<br/>⚡ 성능: 최고"]

    style B1 fill:#ffebee
    style C1 fill:#fff3e0
    style D1 fill:#e8f5e8
    style E1 fill:#e1f5fe

🎯 실제 시스템에서의 선택

pie title 실제 시스템에서 사용되는 방법
    "무시" : 70
    "탐지/복구" : 20
    "회피" : 8
    "예방" : 2

대부분의 현대 운영체제는 "무시" 방법을 사용: - Windows, Linux, macOS 등 - 교착 상태 발생 빈도가 매우 낮음 - 성능 오버헤드를 최소화하는 것이 우선

🔍 교착 상태 vs 기타 문제

문제 유형 특징 해결 방법
교착 상태 영구적 대기 예방/회피/탐지/복구
굶주림 무기한 연기 우선순위 조정, 에이징
라이브락 계속 시도하지만 실패 백오프 알고리즘
우선순위 역전 높은 우선순위가 블록됨 우선순위 상속

연습 문제

🧩 문제 1: 교착 상태 조건 분석

다음 상황에서 교착 상태의 네 가지 조건이 만족되는지 분석하세요:

상황: 두 개의 프린터 P1, P2와 두 개의 프로세스 A, B가 있습니다. - 프로세스 A: P1을 사용 중이며 P2를 요청 - 프로세스 B: P2를 사용 중이며 P1을 요청

답안: 1. ✅ 상호 배제: 프린터는 한 번에 하나의 프로세스만 사용 가능 2. ✅ 점유와 대기: A는 P1을 보유하고 P2를 대기, B는 P2를 보유하고 P1을 대기 3. ✅ 비선점: 프린터는 강제로 빼앗을 수 없음 4. ✅ 순환 대기: A → P2 → B → P1 → A

결론: 네 조건이 모두 만족되므로 교착 상태 발생

🧩 문제 2: 은행가 알고리즘

다음 시스템에서 요청 Request[1] = (1,0,2)가 허용될 수 있는지 은행가 알고리즘으로 판단하세요:

프로세스 | Max  | Allocation | Need | Available
        | A B C | A B C      | A B C | A B C
--------|------|------------|------|-------
   T0   | 7 5 3 | 0 1 0     | 7 4 3 | 3 3 2
   T1   | 3 2 2 | 2 0 0     | 1 2 2 |
   T2   | 9 0 2 | 3 0 2     | 6 0 0 |
   T3   | 2 2 2 | 2 1 1     | 0 1 1 |
   T4   | 4 3 3 | 0 0 2     | 4 3 1 |

답안:

  1. 요청 검증:
  2. Request[1] = (1,0,2) ≤ Need[1] = (1,2,2) ✅
  3. Request[1] = (1,0,2) ≤ Available = (3,3,2) ✅

  4. 가상 할당:

    Available = (3,3,2) - (1,0,2) = (2,3,0)
    Allocation[1] = (2,0,0) + (1,0,2) = (3,0,2)
    Need[1] = (1,2,2) - (1,0,2) = (0,2,0)
    

  5. 안전성 검사:

  6. T1: Need(0,2,0) ≤ Available(2,3,0) ✅ → Work = (5,3,2)
  7. T3: Need(0,1,1) ≤ Available(5,3,2) ✅ → Work = (7,4,3)
  8. T4: Need(4,3,1) ≤ Available(7,4,3) ✅ → Work = (7,4,5)
  9. T2: Need(6,0,0) ≤ Available(7,4,5) ✅ → Work = (10,4,7)
  10. T0: Need(7,4,3) ≤ Available(10,4,7) ✅

안전 순서:
결론: 요청 허용 가능 ✅

🧩 문제 3: 교착 상태 예방

다음 코드에서 순환 대기 조건을 제거하여 교착 상태를 예방하세요:

pthread_mutex_t mutex_A, mutex_B, mutex_C;

void thread1() {
    pthread_mutex_lock(&mutex_A);
    pthread_mutex_lock(&mutex_C);
    // 작업 수행
    pthread_mutex_unlock(&mutex_C);
    pthread_mutex_unlock(&mutex_A);
}

void thread2() {
    pthread_mutex_lock(&mutex_B);
    pthread_mutex_lock(&mutex_A);
    // 작업 수행
    pthread_mutex_unlock(&mutex_A);
    pthread_mutex_unlock(&mutex_B);
}

void thread3() {
    pthread_mutex_lock(&mutex_C);
    pthread_mutex_lock(&mutex_B);
    // 작업 수행
    pthread_mutex_unlock(&mutex_B);
    pthread_mutex_unlock(&mutex_C);
}

답안:

// 뮤텍스에 순서 부여: A < B < C
pthread_mutex_t mutex_A, mutex_B, mutex_C;

void thread1() {
    pthread_mutex_lock(&mutex_A);  // 낮은 순서 먼저
    pthread_mutex_lock(&mutex_C);
    // 작업 수행
    pthread_mutex_unlock(&mutex_C);  // 높은 순서 먼저 해제
    pthread_mutex_unlock(&mutex_A);
}

void thread2() {
    pthread_mutex_lock(&mutex_A);  // 순서 준수
    pthread_mutex_lock(&mutex_B);
    // 작업 수행
    pthread_mutex_unlock(&mutex_B);
    pthread_mutex_unlock(&mutex_A);
}

void thread3() {
    pthread_mutex_lock(&mutex_B);  // 순서 준수
    pthread_mutex_lock(&mutex_C);
    // 작업 수행
    pthread_mutex_unlock(&mutex_C);
    pthread_mutex_unlock(&mutex_B);
}


📚 참고 자료

  • Operating System Concepts - Silberschatz, Galvin, Gagne
  • Modern Operating Systems - Andrew S. Tanenbaum
  • Operating Systems: Internals and Design Principles - William Stallings

🔗 관련 링크


© 2024 Operating Systems Study Guide. 모든 권리 보유.