운영체제 락 개념과 동기화 기법 (4. OS 내부 락과 문제 해결)

2025. 2. 26. 12:25정보기술/운영체제 (OS)

🔍 락의 문제점과 해결 방법 (데드락, 우선순위 역전)

멀티스레딩 환경에서 락(Lock)을 사용할 때 데드락(Deadlock)우선순위 역전(Priority Inversion) 같은 문제가 발생할 수 있습니다.
이를 방지하고 해결하기 위한 운영체제(OS)에서 제공하는 기법들을 학습해보겠습니다.


🎯 학습 목표

  1. 락(Lock)의 주요 문제점:
    • 데드락(Deadlock): 여러 개의 스레드가 서로 Lock을 해제하기를 기다리며 무한 대기 상태에 빠지는 문제.
    • 우선순위 역전(Priority Inversion): 낮은 우선순위 스레드가 높은 우선순위 스레드보다 먼저 Lock을 획득하여 성능 저하를 유발하는 문제.
  2. 해결 방법:
    • 데드락 해결 방법: 예방, 회피, 탐지 기법 학습.
    • OS 커널 락(Kernel Lock) 학습: Linux 커널의 Spinlock, Windows의 SRWLock 이해.

🔴 데드락(Deadlock)이란?

1️⃣ 데드락(Deadlock)의 개념

  • 두 개 이상의 스레드 또는 프로세스가 서로가 가진 자원을 요청하면서 무한 대기 상태에 빠지는 현상.
  • Thread A가 Lock1을 획득하고 Lock2를 기다리는 동안, Thread B가 Lock2를 획득하고 Lock1을 기다리면 서로 기다리면서 영원히 멈추는 상태가 발생.

2️⃣ 데드락 발생 조건 (Coffman 조건)

다음 4가지 조건이 모두 충족되면 데드락이 발생할 수 있음.

  1. 상호 배제(Mutual Exclusion): 한 번에 하나의 스레드만 Lock을 사용할 수 있음.
  2. 점유와 대기(Hold and Wait): 하나의 Lock을 점유한 스레드가 다른 Lock이 해제되길 기다림.
  3. 비선점(No Preemption): Lock을 점유한 스레드는 강제로 Lock을 해제할 수 없음.
  4. 순환 대기(Circular Wait): 여러 개의 스레드가 서로 Lock을 요청하며 순환 구조가 형성됨.

✅ 데드락(Deadlock) 해결 방법

데드락을 해결하는 방법은 크게 예방(Prevention), 회피(Avoidance), 탐지(Detection) 로 나눌 수 있습니다.

1️⃣ 예방(Prevention)

데드락 발생 조건 중 하나라도 제거하여 문제를 방지하는 방법.

  • 해결 방법:
    1. 락 획득 순서 지정(Ordering Locks)
      • 모든 스레드가 Lock을 획득할 때 항상 같은 순서로 Lock을 획득하도록 강제.
    2. 타임아웃 설정
      • 특정 시간이 지나면 Lock을 강제로 해제하여 무한 대기를 방지.
    3. 자원 요청 전 선점(Preemption)
      • 다른 스레드가 Lock을 기다리는 경우, 먼저 점유한 Lock을 해제하도록 설계.

🟢 C 코드 예제 (락 획득 순서 지정)

pthread_mutex_t lock1, lock2;

void* thread_func1(void* arg) {
    pthread_mutex_lock(&lock1);
    pthread_mutex_lock(&lock2); // 항상 lock1 -> lock2 순서로 획득
    printf("Thread 1 실행 중...\n");
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    return NULL;
}

void* thread_func2(void* arg) {
    pthread_mutex_lock(&lock1); // 같은 순서 유지
    pthread_mutex_lock(&lock2);
    printf("Thread 2 실행 중...\n");
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    return NULL;
}

모든 스레드가 같은 순서로 Lock을 획득하면 데드락이 방지됨!


2️⃣ 회피(Avoidance) - 은행가 알고리즘(Banker's Algorithm)

  • 시스템이 사전에 Lock 요청을 분석하여 데드락이 발생할 가능성이 있으면 거부하는 방식.
  • 가장 유명한 알고리즘은 은행가 알고리즘(Banker's Algorithm).
    • 각 스레드가 필요한 Lock의 최대 개수를 미리 정의.
    • 현재 사용 가능한 자원을 분석하여 데드락 가능성이 있는 요청은 거부.

은행가 알고리즘은 주로 운영체제의 자원 관리에서 사용되며, 일반적인 사용자 코드에서 직접 구현되지는 않음.


3️⃣ 탐지(Detection) - 자원 할당 그래프(Resource Allocation Graph)

  • 실제로 데드락이 발생했는지 확인한 후, 해결하는 방식.
  • 시스템에서 자원 할당 그래프(Resource Allocation Graph, RAG) 를 생성하여 순환 대기(Circular Wait) 가 있는지 탐지.
  • 데드락이 발생한 경우, 강제 종료(Kill Process) 또는 자원 회수(Resource Preemption) 를 수행.

데드락 탐지는 시스템 레벨에서 수행되며, 사용자 코드에서는 주로 Lock 타임아웃을 설정하는 방식으로 구현.


✅ OS 커널 락(Kernel Lock)

운영체제 커널은 Lock을 통해 프로세스 간 동기화를 수행합니다.
대표적인 커널 락 기법으로 Linux의 Spinlock과 Windows의 SRWLock 이 있습니다.


🔹 Linux 커널의 Spinlock

스핀락(Spinlock)락이 해제될 때까지 CPU를 계속 사용하며 대기하는 락입니다.
운영체제 커널에서 자주 사용되며, 짧은 시간 동안 Lock이 유지될 때 유리합니다.

Linux 커널에서 spinlock_t 사용 예제

#include <linux/spinlock.h>

spinlock_t my_lock; // 스핀락 선언

void my_function() {
    spin_lock(&my_lock); // 🔒 스핀락 획득
    // 크리티컬 섹션 실행
    spin_unlock(&my_lock); // 🔓 스핀락 해제
}

특징

  • 멀티코어 환경에서 커널 내 공유 데이터 보호에 사용.
  • 짧은 Lock 유지 시간에 적합, 긴 Lock 유지 시 CPU 낭비 발생 가능.

🔹 Windows의 SRWLock (Slim Read-Write Lock)

Windows 운영체제는 SRWLock(Slim Read-Write Lock) 을 제공하여 읽기-쓰기 락을 커널 수준에서 지원합니다.

Windows의 SRWLock 사용 예제

#include <windows.h>

SRWLOCK srwLock; // SRWLock 선언

void reader_function() {
    AcquireSRWLockShared(&srwLock); // 🔒 읽기 락 획득
    // 읽기 작업 수행
    ReleaseSRWLockShared(&srwLock); // 🔓 락 해제
}

void writer_function() {
    AcquireSRWLockExclusive(&srwLock); // 🔒 쓰기 락 획득
    // 쓰기 작업 수행
    ReleaseSRWLockExclusive(&srwLock); // 🔓 락 해제
}

특징

  • Windows에서 읽기-쓰기 락을 최적화하여 제공.
  • 읽기 작업이 많은 경우 성능 향상 가능.

✅ 정리

문제점 원인 해결 방법
데드락(Deadlock) 스레드 간 Lock 대기 순환 발생 Lock 획득 순서 지정, 타임아웃 설정, 은행가 알고리즘 사용
우선순위 역전(Priority Inversion) 낮은 우선순위 스레드가 Lock을 점유 우선순위 상속(Priority Inheritance) 기법 활용
커널 락(Kernel Lock) 커널 내 동기화 문제 Linux: Spinlock, Windows: SRWLock 사용