임계 구역 (Critical Section)
임계 구역은 여러 스레드 또는 프로세스가 동시에 접근할 수 없는 코드 블록을 말합니다. 이는 공유 자원에 접근하는 코드에서 동시 실행으로 인해 잘못된 동작이 발생할 수 있는 부분을 보호하는 역할을 합니다. 즉, 여러 작업이 경쟁 상태(Race Condition)를 일으킬 수 있는 부분을 임계 구역으로 설정하여 동기화합니다.
임계 구역에서는 하나의 스레드 또는 프로세스만 접근할 수 있으며, 다른 스레드나 프로세스는 그 구역이 해제될 때까지 기다려야 합니다. 이로 인해 공유 자원에 대한 동시 접근이 제한되어, 데이터의 일관성과 무결성을 유지할 수 있습니다.
임계 구역의 주요 요구 사항
임계 구역을 설정할 때는 다음과 같은 조건을 만족해야 합니다.
- 상호 배제(Mutual Exclusion):
- 한 번에 하나의 스레드만 임계 구역에 들어갈 수 있어야 합니다.
- 다른 스레드는 해당 스레드가 임계 구역을 빠져나올 때까지 기다려야 합니다.
- 진행 조건(Progress):
- 임계 구역에 진입하고자 하는 스레드가 있을 경우, 다른 스레드가 임계 구역을 빠져나오면 즉시 그 스레드가 들어갈 수 있어야 합니다. 대기 중인 스레드가 없다면 진입이 가능해야 합니다.
- 유한 대기(Bounded Waiting):
- 어떤 스레드도 임계 구역에 무한히 대기하지 않아야 합니다.
- 특정 스레드가 계속해서 진입하지 못하는 기아 상태(Starvation)를 방지해야 합니다.
임계 구역에서 발생할 수 있는 문제
- Race Condition: 여러 스레드가 동시에 임계 구역에 접근하여 공유 자원을 수정할 경우, Race Condition이 발생할 수 있습니다.
- Deadlock(교착 상태): 두 개 이상의 스레드가 서로 임계 구역에서 대기하는 상황에서, 서로가 상대방의 자원을 기다리며 무한 대기에 빠질 수 있습니다.
- Starvation(기아 상태): 특정 스레드가 계속해서 임계 구역에 진입하지 못하고 무한 대기에 빠지는 상황입니다.
이를 방지하기 위해 동기화 기법을 통해 임계 구역에 대한 안전한 접근을 보장합니다.
임계 구역 설정을 위한 동기화 기법
임계 구역을 보호하기 위한 동기화 기법에는 여러 가지가 있으며, 주로 다음과 같은 방법들이 사용됩니다.
- 뮤텍스(Mutex):
- 뮤텍스는 하나의 스레드만 임계 구역에 접근할 수 있도록 제한하는 잠금 메커니즘입니다.
- 다른 스레드는 뮤텍스가 해제될 때까지 대기해야 합니다.
pthread_mutex_lock(&mutex); // 뮤텍스 잠금
// 임계 구역 코드
pthread_mutex_unlock(&mutex); // 뮤텍스 해제
- 세마포어(Semaphore):
- 세마포어는 뮤텍스와 비슷하지만, 동시 접근 가능한 스레드의 개수를 조정할 수 있습니다.
- 세마포어 값을 이용해 여러 스레드가 임계 구역에 접근할 수 있는 수를 제어합니다.
sem_wait(&sem); // 세마포어 잠금
// 임계 구역 코드
sem_signal(&sem); // 세마포어 해제
- 모니터(Monitor):
- 모니터는 임계 구역을 자동으로 관리해 주는 고급 동기화 기법으로, 임계 구역에 들어갈 수 있는 스레드를 제어합니다.
- 주로 Java나 C# 같은 고급 언어에서 사용됩니다.
- 스핀락(Spinlock):
- 스핀락은 임계 구역에 들어갈 수 있을 때까지 짧은 반복(바쁜 대기)을 수행합니다. 이는 잠금이 곧 해제될 것으로 예상되는 경우에 유용하지만, 대기 시간이 길어지면 비효율적입니다.
while (__sync_lock_test_and_set(&lock, 1)) {
// 잠금 대기 중 (busy-waiting)
}
// 임계 구역 코드
__sync_lock_release(&lock); // 잠금 해제
임계 구역을 사용하는 동기화 예시 (C언어)
문제 상황: 두 스레드가 동시에 공유 자원에 접근하려고 할 때, 임계 구역을 통해 Race Condition을 방지하는 예시입니다.
#include <stdio.h>
#include <pthread.h>
int shared_var = 0; // 공유 자원
pthread_mutex_t mutex; // 뮤텍스 선언
void* thread_function(void* arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&mutex); // 뮤텍스 잠금 (임계 구역 진입)
shared_var++; // 공유 자원 수정
pthread_mutex_unlock(&mutex); // 뮤텍스 해제 (임계 구역 종료)
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_mutex_init(&mutex, NULL); // 뮤텍스 초기화
// 두 스레드 생성
pthread_create(&t1, NULL, thread_function, NULL);
pthread_create(&t2, NULL, thread_function, NULL);
// 두 스레드 종료 대기
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// 최종 결과 출력
printf("최종 공유 자원 값: %d\n", shared_var);
pthread_mutex_destroy(&mutex); // 뮤텍스 제거
return 0;
}
설명:
- pthread_mutex_lock(&mutex):
shared_var
에 접근하기 전에 뮤텍스를 잠급니다. 이를 통해 임계 구역에 하나의 스레드만 접근할 수 있도록 만듭니다. - pthread_mutex_unlock(&mutex):
shared_var
에 대한 작업이 끝나면 뮤텍스를 해제하여 다른 스레드가 임계 구역에 접근할 수 있도록 합니다.
이 프로그램에서 뮤텍스를 사용하지 않으면 Race Condition이 발생하여 shared_var
의 최종 값이 예상한 값과 달라질 수 있습니다. 그러나 뮤텍스를 사용함으로써 임계 구역에 대한 접근이 안전하게 이루어집니다.
결론
- 임계 구역은 공유 자원에 대한 동시 접근을 방지하기 위해 설정된 코드 블록입니다.
- Race Condition을 방지하기 위해 뮤텍스, 세마포어, 스핀락 등 다양한 동기화 기법이 임계 구역을 보호하는 데 사용됩니다.
- 적절한 임계 구역 설정은 데이터 무결성을 보장하고, 동시성 프로그래밍에서 중요한 역할을 합니다.
'Interview > OS' 카테고리의 다른 글
OS Disk Swap 디스크 스왑 (0) | 2024.09.28 |
---|---|
OS Delayed Write 지연 쓰기 (0) | 2024.09.28 |
OS 페이징과 캐시 (0) | 2024.09.28 |
Multiprocess간 IPC 중 메모리 매핑 파일 (1) | 2024.09.28 |
Multiprocess간 IPC 중 공유 메모리 (0) | 2024.09.28 |