May 28, 2020
참고도서: Operating System Concepts (10/E) Abraham Silberschatz, Peter B. Galvin, Greg Gagne
교착상태는 일반적으로 지금까지 다뤄왔던 자원을 점유하는 상황 속에서 발생하는 resource deadlock 과 스레드간 통신 상황에서 발생하는 communication deadlock 으로 나뉜다. Communication deadlock은 정형화된 해결안이나 알고리즘이 없어 우리는 이번에도 역시 resource deadlock을 해결하는 방법에 대해서 다루게될 것이다.
이 알고리즘은 프로세스가 자원을 점유할 때마다 그래프를 만들어서 사이클이 존재하는지 확인하는 방법이다. 뜬금없이 사이클이 왜 나오나 싶겠지만 이 알고리즘에서 사이클이 존재한다는 것은 자원을 점유한 상태에서 두 프로세스가 서로에게 자원을 요청하는 상태를 가르킨다. 알고리즘을 조금 더 자세히 보면 이해가 될 것이다. 프로세스가 실행중에 자원을 점유하고 해제할 때, 다음과 같은 과정으로 그래프를 만들게 된다.
다음 예시를 통해 교착상태가 발생할 가능성이 있는지 확인해보자.
Thread 1
lock(X);
...
lock(Y);
...
unlock(Y);
unlock(X);Thread 2
lock(Y);
...
lock(X);
...
unlock(X);
unlock(Y);먼저 두 스레드가 실행되면서 각 명령어가 실행되는 순서에는 차이가 있을 수 있다. 그렇기 때문에 교착상태는 항상 발생하는 것이 아니고 특정한 상황이 성립되었을 때만 발생한다. 위 두 개의 스레드도 얼마든지 다른 순서로 실행될 수 있지만 다음과 같이 실행되는 상황을 생각해보자
lock(X)) : 이때 Cyclic Deadlock Monitoring Algorithm에 의해 Nx 의 노드가 하나 만들어진다.lock(Y)) : 이때 역시 새로운 점유가 발생했기 때문에 Ny 노드를 하나 생성한다.lock(Y)) : 이미 lock X 를 점유한 상태에서 lock Y의 점유를 시도하기 때문에, 알고리즘에 따라 Nx와 Ny 사이에 간선을 만든다.lock(X)) : 이미 lock Y 를 점유한 상태에서 lock X의 점유를 시도하기 때문에, 알고리즘에 따라 Ny와 Nx 사이에 간선을 만든다.이때 두 스레드가 만든 그래프 안에 사이클이 발생했기 때문에, 프로그램은 교착상태에 대한 경고를 사용자에게 알릴 수 있다.
위에서 사용한 알고리즘은 실제로 교착상태가 발생하는 순서로 프로세스가 실행됐을 때만 교착상태를 판단할 수 있다. 그렇다면 잠재되어 있는 교착상태의 위험에 대해 대처할 수 있는 알고리즘은 없을까? Potentail Cyclic Deadlock Detection Algorithm 은 이름이 말해주는 것처럼 교착상태가 일어날 수 있는 상황을 판별하는 알고리즘이다. 위 알고리즘과 큰 차이는 없지만 lock을 해제할 때, 간선을 삭제해주지 않고 그대로 유지한다는 점이 다르다. 따라서 이 알고리즘의 흐름을 정리해보면 다음과 같다.
이렇게 하면 한번 만들어진 사이클은 프로세스가 종료될 때까지 남아있기 때문에, 실제로 교착상태가 발생하지 않았다고 해도 사용자에게 교착상태가 일어날 위험성이 있다는 것을 알릴 수 있다.
기존 알고리즘을 보완한 Potential Cyclic Deadlock Detection Algorithm을 사용하면 완벽하게 교착상태의 발생 가능성을 막을 수 있을 것 같지만, 실상은 그렇지 않다. 실제로는 교착상태가 일어날 일이 없는데, 사이클 알고리즘이 교착상태로 오탐지할 수 있는 경우를 알아보자.
Thread 1
lock(X);
lock(Y);
...
unlock(Y);
unlock(X);
lock(Y);
lock(X);
...
unlock(X);
unlock(Y)위와 같이 한 스레드 안에서 두 번에 걸쳐 자원을 점유하는 로직이 있다고 해보자. 이 스레드는 가장 처음에 lock X를 점유한 채로 lock Y을 획득하려고 하기 때문에 X에 대한 노드와 해당 노드로 부터 Y노드로 향하는 간선을 가지게 된다. 그리고 작업을 마친 뒤 X와 Y를 해제하게 되는데, 이때 Potential Cyclic Deadlock Detection Algorithm 은 만들어진 간선을 해제하지 않기 때문에 노드와 간선이 남아있는 채로 계속 스레드가 실행된다.
실행되던 스레드는 lock Y 를 획득하고 lock X를 점유하려고 한다. 이때 노드 Y가 생성되고 노드 Y로부터 노드 X로 연결되는 간선이 만들어진다. 즉, 사이클이 만들어지는 것이다.
하지만 한 스레드 안에서는 교착상태가 발생할 수 없다. 따라서 이 경우에는 교착상태가 아니지만 알고리즘이 교착상태라고 오인하는 False Positive 가 발생한다.
Thread 1
lock(X);
lock(Y);
lock(Z);
...
unlock(Z);
unlock(Y);
unlock(X);Thread 2
f2(){
lock(X);
lock(Z);
lock(Y);
...
unlock(Y);
unlock(Z);
unlock(Y);
}위 같이 두 개의 스레드가 실행되고 있다고 해보자. 중첩된 lock들을 보면 우리가 흔히 알고 있는 교착상태 패턴이다. Thread 1은 Y를 점유하는 중에 Z를 요구하게되고, Thread 2는 Z를 점유하는 중에 Y를 요구하게 된다.
하지만 두 스레드 모두 중첩된 lock을 동일한 lock X 로 감싸고 있다. 따라서 Thread 1 이 lock을 획득하게되면, Thread 2는 lock Y 와 Z가 있는 구간에 절대 도달하지 못하기 때문에 상호배제가 보장되게 된다. 이렇게 두 스레드에서 사용되는 lock 들의 상호배제를 보장하는 lock을 Gate Lock(혹은 Guard Lock) 이라고 한다.
이 경우에도 교착상태는 절대로 발생하지 않지만, 알고리즘으로 사이클을 검사하게 되면 X와 Y의 관계 때문에 사이클이 발생하게된다.
Thread 1
lock(X);
lock(Y);
unlock(Y);
unlock(X);
start(f2);Thread 2
lock(Y);
lock(X);
unlock(X);
unlock(Y);위 경우에서도 X 와 Y 사이에서 교착상태가 일어나는 패턴이 발견된다. 그렇기 때문에 알고리즘은 여전히 사이클이 있는 그래프를 만들게 될 것이다.
하지만 상식적으로 생각해보자. 교착상태는 실행 중인 두 프로세스나 스레드가 서로의 자원을 기다리게 되기 때문에 발생한다. 위 코드를 보면 Thread 1의 자원 점유와 해제 가 모두 끝난 뒤에 Thread 2를 생성한다. 따라서 Thread 1 과 Thread 2는 절대 병렬적으로 실행될 수 없는 스레드이지만 교착상태가 있을 것이라고 오탐지하는 것이다.
GoodLock 알고리즘은 기존 Cyclic Deadlock Detection 알고리즘의 문제점이었던 오탐지 상황을 모두 보완한 알고리즘이다. 사이클을 탐지한다는 점에서 큰 그림은 비슷하지만 몇가지 다른 부분들이 있다.
GoodLock 알고리즘은 앞서 설명했던 사이클의 세 가지 예외상황을 탐지하기 위해서 간선을 만들 때 몇가지 정보를 더 추가해서 간선은 만들게 되는데, 다음과 같이 정보를 구성한다.
(노드1, (노드1의 코드 섹션, 간선을 만든 스레드의 아이디, Guard Lock 위치, 노드2의 코드 섹션) 노드2)
헷갈리니까 좀 더 자세히 보자. 그리고 기억해야할 것은 사이클이 발생했다는 것은 위 간선 정보가 한 쌍으로 만들어진다는 것이다.
GoodLock 알고리즘은 그래프에서 사이클이 발생했을 때 위 정보를 가지고 해당 사이클이 교착상태에 유효한 사이클인지 확인하게 된다. 그럼 앞서서 지적되었던 false positive 상황들을 검사해보자.
(m11, (s11, t1, G1, s12),
m12)
(m21, (s21, t2, G2,
s22), m22)
위와 같이 두개의 노드가 사이클을 만들었다고 해보자. 한 스레드 안에서는 병렬적으로 작업이 실행되는 것이 불가능하기 때문에 한 스레드안에서 만들어지는 사이클은 무의미하다. 따라서 두 간선 정보에서 스레드 아이디를 의미하는 t1과 t2 가 같은 값을 가지는지 확인하면 false positive 상황인지 쉽게 판별할 수 있게된다.
게이트 락의 여부역시 두 간선이 가지는 G의 값을 확인하는 것으로 해결할 수 있다. 만약 두 G가 가지는 위치가 겹친다면, 둘은 같은 곳에 위치해 있다는 의미이기 때문에 스레드간의 상호배제를 보장하는 게이트 락의 역할을 하게된다.
간선정보에 포함된 s 값은 간선이 만들어진 코드섹션을 의미한다. 만약에 먼저 만들어진 간선의 코드섹션이 뒤에 만들어진 간선보다 앞에 있다면 두 간선은 병렬적으로 처리되면서 만들어진 것이 아니라 순차적으로 처리된 코드에서 만들어졌음을 의미한다. 따라서 이런 경우에는 false positive 상황으로 판단하게 된다.