교착상태 (Deadlock)

둘 이상의 프로세스가 서로가 가진 자원을 기다리며 무한정 대기하는 상황

Deadlock
Source: https://www.geeksforgeeks.org/deadlock-system-model/

교착상태를 시뮬레이션하는 예제:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import threading
import time

class Resource:
    def __init__(self, name):
        self.name = name
        self.lock = threading.Lock()
    
    def acquire(self, process_name):
        print(f"{process_name}{self.name} 획득 시도")
        self.lock.acquire()
        print(f"{process_name}{self.name} 획득 성공")
    
    def release(self, process_name):
        print(f"{process_name}{self.name} 반환")
        self.lock.release()

def process_task(process_name, first_resource, second_resource):
    """
    교착상태를 발생시키는 프로세스 작업을 시뮬레이션합니다.
    각 프로세스는 두 개의 자원을 순차적으로 획득하려 시도합니다.
    """
    try:
        # 첫 번째 자원 획득
        first_resource.acquire(process_name)
        print(f"{process_name}가 작업 중…")
        time.sleep(1)  # 다른 프로세스가 두 번째 자원을 획득할 시간을 줌
        
        # 두 번째 자원 획득 시도
        second_resource.acquire(process_name)
        print(f"{process_name}가 모든 자원 획득 성공")
        
        # 작업 수행
        time.sleep(1)
        
        # 자원 반환
        second_resource.release(process_name)
        first_resource.release(process_name)
        
    except Exception as e:
        print(f"{process_name} 오류 발생: {e}")

def main():
    # 두 개의 자원 생성
    resource_A = Resource("Resource A")
    resource_B = Resource("Resource B")
    
    # 두 개의 프로세스 생성
    # Process 1은 A -> B 순서로 자원 획득 시도
    # Process 2는 B -> A 순서로 자원 획득 시도
    process1 = threading.Thread(
        target=process_task,
        args=("Process 1", resource_A, resource_B)
    )
    process2 = threading.Thread(
        target=process_task,
        args=("Process 2", resource_B, resource_A)
    )
    
    # 프로세스 시작
    process1.start()
    process2.start()
    
    # 프로세스 종료 대기
    process1.join()
    process2.join()

if __name__ == "__main__":
    print("교착상태 시뮬레이션 시작")
    main()
    print("시뮬레이션 종료")

Deadlock이 발생하기 위한 필요 조건

Deadlock이 발생하기 위해서는 다음 네 가지 조건이 모두 충족되어야 한다:

  1. 상호 배제(Mutual Exclusion)
    하나의 자원은 한 번에 하나의 프로세스만 사용할 수 있다.

    1
    
    lock = threading.Lock()  # 한 번에 하나의 스레드만 획득 가능
    
  2. 점유와 대기(Hold and Wait)
    자원을 가진 프로세스가 다른 자원을 기다리는 상태가 발생한다.

    1
    2
    
    with self.lock:  # 첫 번째 락을 보유한 상태에서
        with other.lock:  # 두 번째 락을 기다림
    
  3. 선점 불가(No Preemption)
    다른 프로세스의 자원을 강제로 빼앗을 수 없다.

  4. 순환 대기(Circular Wait)
    프로세스들이 순환적으로 서로의 자원을 기다린다.

교착상태 해결책 및 방지책

교착상태를 해결하거나 방지하기 위한 여러 방법이 있다:

예방(Prevention)

교착상태 발생 조건 중 하나 이상을 제거하여 교착상태를 방지한다:

  • 상호 배제 부정: 자원을 공유 가능하게 만든다.
  • 점유와 대기 부정: 프로세스가 실행되기 전 모든 필요한 자원을 할당받도록 한다.
  • 비선점 부정: 자원을 강제로 회수할 수 있게 한다.
  • 순환 대기 부정: 자원에 번호를 부여하고 오름차순으로만 요청하도록 한다.

회피(Avoidance)

시스템의 상태를 지속적으로 검사하여 안전한 상태를 유지한다.
대표적인 방법으로 은행원 알고리즘(Banker’s Algorithm)이 있다.

탐지 및 복구(Detection and Recovery)

교착상태를 탐지하고, 발생 시 복구하는 방법이다:

  • 프로세스 종료: 교착상태에 있는 프로세스를 종료한다.
  • 자원 선점: 교착상태에 있는 프로세스로부터 자원을 강제로 회수한다.

실제 시스템에서의 교착상태 예방 전략

  1. 시스템 설계 단계에서의 예방

    • 자원 할당 순서 정의
    • 자원 사용 시간 제한
    • 작업 우선순위 설정
    • 자원 할당 그래프 관리
  2. 운영 단계에서의 모니터링

    • 자원 사용량 실시간 모니터링
    • 교착상태 감지 시스템 구축
    • 로그 분석 및 패턴 파악
  3. 복구 전략 수립

    • 체계적인 프로세스 종료 절차
    • 자원 회수 메커니즘
    • 백업 시스템 운영

실제 사례와 해결 방안

  1. 데이터베이스 시스템의 교착상태:
    1. 트랜잭션 타임아웃 설정
    2. 락 에스컬레이션 방지
    3. 트랜잭션 격리 수준 적절히 설정
  2. 운영체제의 교착상태:
    1. 자원 할당 알고리즘 최적화
    2. 프로세스 우선순위 동적 조정
    3. 자원 사용 모니터링 강화

고려사항 및 주의사항

  1. 성능과 안전성 균형

    • 과도한 교착상태 방지 메커니즘은 성능 저하 초래
    • 시스템 특성에 맞는 적절한 수준 설정 필요
  2. 확장성 고려

    • 시스템 규모 증가에 따른 교착상태 위험 증가
    • 확장 가능한 모니터링 시스템 구축
  3. 테스트 및 검증

    • 다양한 시나리오에서의 교착상태 테스트
    • 정기적인 시스템 점검 및 모니터링

모범 사례

  1. 자원 할당 전략

    • 필요한 최소한의 자원만 할당
    • 자원 사용 시간 최소화
    • 자원 해제 즉시 수행
  2. 모니터링 및 로깅

    • 상세한 로그 기록 유지
    • 실시간 모니터링 시스템 구축
    • 주기적인 시스템 상태 점검
  3. 시스템 설계

    • 모듈화된 설계로 교착상태 영향 범위 최소화
    • 명확한 자원 할당 정책 수립
    • 효율적인 복구 메커니즘 구현

참고 및 출처