교착상태(Deadlock)은 서로 자원을 요구하기만 하고 처리하지를 못하는 상태이다. 이에 대한 해결방법으로 예방, 회피, 감지 및 복구, 무시가 있다.
: 자원을 소유한 쓰레드(프로세스)들 사이에서, 각 쓰레드는 다른 쓰레드가 소유한 자원을 요청하여 무한정 대기하고 있는 현상
-> 다중 프로그래밍 시스템 초기에 노출된 문제점이다.
다음 그림처럼 각 쓰레드가 다른 쓰레드가 소유하고 있는 자원을 요청하면서 환형 요청/대기(circular wait)을 이루게 되었다. 환형 고리는 스스로 인식이나 해제가 불가하다.
다음은 교착상태를 확인해볼 수 있는 코드이다. 두 쓰레드가 각각 락을 획득한 이후에 상대의 락을 기다리는 상황이다. 서로가 서로의 락을 기다리며 무한 대기하게 된다.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int x = 0; // shared
int y = 0; // shared
pthread_mutex_t lock1; // mutex lock
pthread_mutex_t lock2; // mutex lock
void* thread1(void* arg) {
pthread_mutex_lock(&lock1); // lock1 for x
printf("lock thread1 lock1 \n");
x++;
sleep(2);
pthread_mutex_lock(&lock2);
printf("unlock thread1 lock2\n");
y++;
pthread_mutex_unlock(&lock2); // unlock lock2
printf("unlock thread1 lock2 \n");
pthread_mutex_unlock(&lock1); // unlock lock1
printf("unlock thread1 lock1\n");
}
void* thread2(void* arg) {
pthread_mutex_lock(&lock2); // lock2 for y
printf("lock thread2 lock2\n");
y++;
sleep(2);
pthread_mutex_lock(&lock1); // lock1 for x
printf("lock thread2 lock1\n");
x++;
pthread_mutex_unlock(&lock1); // unlock lock1
printf("unlock thread2 lock1 \n");
pthread_mutex_unlock(&lock2); // unlock lock2
printf("unlock thread2 lock2 \n");
}
int main() {
pthread_t tid[2];
pthread_mutex_init(&lock1, NULL);
pthread_mutex_init(&lock2, NULL);
pthread_create(&tid[0], NULL, thread1, NULL);
pthread_create(&tid[1], NULL, thread2, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&lock2);
pthread_mutex_destroy(&lock1);
printf("x = %d, y = %d \n", x, y);
return 0;
}
![]() |
![]() |
: 자원에 대한 시스템의 상태를 나타내는 방향성 그래프
이러한 요소를 자원할당그래프로부터 확인하고 교착상태를 판단할 수 있다.
그래프는 정점과 간선으로 이루어져 있다.
다음 4가지 상황이 허용되는 시스템은 언제든 교착상태가 발생할 수 있다.
자원적 특성
행위적 특성
1. 교착상태 예방(prevention)
2. 교착상태 회피(avoidance)
3. 교착상태 감지 및 복구(detection and recovery)
4. 교착상태 무시
코프만의 4가지 조건 중 최소 하나를 성립하지 못하게 한다.
1. 상호 배재 조건 -> 상호 배제 없애기
2. 비선점 조건 -> 선점 허용
=> 자원적 특성을 제한하기는 어렵다.
3. 점유와 대기 조건 -> 기다리지 않게 한다.
(방법1) 운영체제는 쓰레드 실행 전 필요한 모든 자원을 파악하고 실행시 한 번에 할당
(방법2) 쓰레드가 새로운 자원을 요청하려면, 현재 할당 받은 모든 자원을 반환하고, 한꺼번에 요청하여 할당
문제점
4. 환형 대기(Circular Wait) 조건 -> 환형 대기 제거
모든 자원에 숫자를 부여하고 숫자가 큰 방향으로만 자원을 할당한다.
예: 마우스를 할당받은 상태에서 프린터를 할당받을 수는 있지만 프린터를 할당받은 상태에서 마우스나 하드디스크를 할당받을 수 없다.
예: 프로세스 P2는 자원을 할당받을 수 없어 강제 종료되고 프로세스 P1은 정상적으로 실행된다.
문제점
=> 점유와 대기, 원형 대기는 프로세스 작업 방식을 제한하고 자원을 낭비하기 때문에 사용이 어렵다.
안전한 상태 Safe state - 현재 프로세스들을 어떤 순서로 실행시켰을 때, 모든 프로세스들이 자신이 요청하는 자원을 가지고 실행할 수 있다면 안전한 상태
불안전한 상태 Unsafe state - 환형 대기에 빠질 수 있는 상태
-> 할당된 자원이 적으면 안정 상태가 크고, 할당된 자원이 늘어날수록 불안정 상태가 커짐
자원 할당 전에 미래에 교착상태 발생여부를 판단한다.
알고리즘
고려 요소
하지만 이런 알고리즘은 비현실적이다.
교착상태를 감지하는 프로그램을 통해, 형성된 교착상태를 푼다. 백그라운드에서 교착상태를 감지하는 프로그램을 늘 실행한다.
자원 할당 그래프를 이용한 교착 상태 검출
타임아웃을 이용한 교착 상태 검출
자원 강제 선점(preemption)
롤백(rollback)
프로세스/쓰레드 강제 종료(kill process)
프로세스를 강제로 종료하는 방법
교착상태는 반드시 발생하지만 자주 발생하지는 않는다. 하지만 발생횟수/확률에 비해 해결에는 상대적으로 많은 비용이 든다.
타조 알고리즘
(문제)
교착상태가 발생하면 시스템 재시작 혹은 특정 프로세스/쓰레드를 강제 종료하면서 관련된 데이터를 잃어버릴 수 있다. (하지만 전체적으로 크지 않은 손실)
예: 데이터베이스