운영체제 락 개념과 동기화 기법 (1. 운영체제에서 동기화 / 1-1. 동기화(Synchronization)란?)

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

📖 1-1. 동기화(Synchronization)란?

운영체제(OS)에서 동기화(Synchronization)여러 개의 프로세스 또는 스레드가 공유 자원(Shared Resource)에 안전하게 접근할 수 있도록 하는 기법입니다.
멀티태스킹 환경에서 경쟁 상태(Race Condition)와 데이터 무결성(Data Integrity) 문제를 해결하기 위해 필수적입니다.


✅ 운영체제(OS)에서 동기화란?

운영체제는 멀티태스킹(Multitasking) 기능을 지원하며, 여러 개의 프로세스 또는 스레드가 동시에 실행될 수 있는 환경을 제공합니다.
하지만, 이때 여러 스레드가 동일한 자원(변수, 파일, 메모리 등)에 접근할 경우 문제가 발생할 수 있습니다.
이를 방지하고 안전한 데이터 처리를 보장하기 위해 동기화(Synchronization) 가 필요합니다.

🔹 동기화가 필요한 주요 이유

이유  설명
경쟁 상태(Race Condition) 방지 여러 개의 스레드가 동시에 같은 자원에 접근할 때 예측할 수 없는 결과가 발생할 수 있음.
데이터 무결성(Data Integrity) 유지 공유 데이터를 여러 스레드가 수정할 경우 데이터 손상(Data Corruption) 발생 가능.
임계 영역(Critical Section) 보호 특정 코드 블록에서 한 번에 하나의 스레드만 실행되도록 보장해야 함.
데드락(Deadlock) 방지 두 개 이상의 스레드가 서로의 자원을 기다리면서 무한 대기 상태에 빠지는 문제 해결.

✅ 동기화가 필요한 이유

운영체제는 멀티스레딩(Multithreading) 환경을 제공하여 여러 작업을 동시에 수행할 수 있도록 합니다.
하지만, 적절한 동기화가 없으면 스레드 간 데이터 충돌(Data Corruption) 문제가 발생할 수 있습니다.
이를 이해하기 위해 은행 계좌 잔액 업데이트 문제를 살펴보겠습니다.

🛑 동기화가 없는 코드 예제 (문제 발생 가능)

#include <stdio.h>
#include <pthread.h>

int balance = 1000; // 공유 자원

void* withdraw(void* arg) {
    balance -= 500; // 두 스레드가 동시에 실행되면 결과가 비정상적일 수 있음
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, withdraw, NULL);
    pthread_create(&t2, NULL, withdraw, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("최종 잔액: %d\n", balance); // 결과가 500이 아닐 수도 있음!
    return 0;
}

📌 실행 결과 (예측 불가한 결과 발생)

최종 잔액: 500
최종 잔액: 1000
최종 잔액: 0  <-- 의도하지 않은 결과 발생 가능!
  • 두 개의 스레드가 동시에 balance -= 500을 실행할 경우, 결과가 예측할 수 없는 값이 될 수 있음.
  • 원하는 결과는 500이어야 하지만, 0이 될 수도 있고, 심지어 1000이 유지될 수도 있음.
  • 이는 경쟁 상태(Race Condition) 때문에 발생하는 문제.

❌ 경쟁 상태(Race Condition)

경쟁 상태(Race Condition)는 두 개 이상의 스레드가 동시에 공유 자원에 접근하여, 실행 순서에 따라 예측할 수 없는 결과를 초래하는 현상입니다.
즉, 한 스레드가 데이터를 수정하는 도중에 다른 스레드가 동일한 데이터를 변경하면 데이터가 손상될 가능성이 있음.

🛑 경쟁 상태 발생 예시

  1. Thread 1 이 balance -= 500 연산을 수행하기 위해 balance 값을 가져옴.
  2. Thread 2 도 거의 동시에 balance -= 500 연산을 수행하기 위해 balance 값을 가져옴.
  3. 두 스레드가 연산을 수행한 후, 새로운 balance 값을 저장하는데, 이전 상태가 반영되지 않은 채 덮어씌워질 수 있음.

예제 시나리오에서 문제 가능성

  • 두 개의 스레드가 동일한 값을 읽고 연산을 수행하면서 중간 연산 결과가 사라지는 문제가 발생.
  • 의도한 값(500)이 나오기도 하지만, 예상치 못한 오류가 발생할 가능성이 매우 높음.

✅ 해결 방법

이러한 문제를 해결하기 위해 운영체제에서 제공하는 동기화 기법을 활용해야 합니다.
대표적인 동기화 방법은 다음과 같습니다.

기법  설명
Mutex (뮤텍스) 한 번에 하나의 스레드만 Lock을 획득하도록 하여 경쟁 상태를 방지
RLock (재진입 가능 락) 같은 스레드가 여러 번 Lock을 획득할 수 있도록 허용
RWLock (읽기-쓰기 락) 여러 스레드가 동시에 읽을 수 있도록 하면서, 쓰기는 단 하나만 가능
Spinlock (스핀락) Lock이 해제될 때까지 CPU가 반복해서 확인하는 방식
Semaphore (세마포어) 특정 개수의 스레드만 공유 자원에 접근할 수 있도록 제한

✅ 뮤텍스를 활용한 동기화 해결 방법

경쟁 상태를 방지하려면 뮤텍스(Mutex, Mutual Exclusion) 를 사용하여 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 해야 합니다.
다음 코드는 pthread_mutex_t를 사용하여 동기화 문제를 해결하는 방법을 보여줍니다.

#include <stdio.h>
#include <pthread.h>

int balance = 1000; // 공유 자원
pthread_mutex_t lock; // 뮤텍스 선언

void* withdraw(void* arg) {
    pthread_mutex_lock(&lock);  // 🔒 락 획득
    balance -= 500;
    pthread_mutex_unlock(&lock); // 🔓 락 해제
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_mutex_init(&lock, NULL); // 뮤텍스 초기화

    pthread_create(&t1, NULL, withdraw, NULL);
    pthread_create(&t2, NULL, withdraw, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("최종 잔액: %d\n", balance); // 항상 500으로 올바르게 출력됨

    pthread_mutex_destroy(&lock); // 뮤텍스 제거
    return 0;
}

📌 결과

최종 잔액: 500  ✅ (예측 가능한 결과)
  • 🔒 pthread_mutex_lock()을 사용하여 공유 자원 보호.
  • 🔓 pthread_mutex_unlock()을 사용하여 락 해제.
  • 이제 경쟁 상태 없이 안전한 동기화가 이루어짐!

✅ 정리

  • 동기화(Synchronization)는 운영체제에서 여러 개의 스레드가 공유 자원에 안전하게 접근할 수 있도록 하는 필수 기법.
  • 동기화가 없으면 경쟁 상태(Race Condition) 가 발생하여 데이터 무결성(Data Integrity) 이 깨질 수 있음.
  • Mutex, Semaphore, Spinlock 등의 락을 사용하여 동기화를 구현할 수 있음.