운영체제/동시성

운영체제, lock에 관해 알아보자 4편 - 데드락

Recfli 2024. 3. 31. 00:03

[ 비 데드락 버그 ]

 비 데드락 버그에 관해서 다루는 파트가 있다. 그 부분은 Lock과 Condition Valiable을 제대로 사용하지 않아서 일어나는 버그들이다. 

 

예시로는 첫 번째 예시로는 Lock을 제대로 쓰지 않아서 Null예외가 뜨는 경우가 있다. 코드로 나타내면 이런 상황이다. 멀티쓰레드 환경에서 쓰레드 내의 proc_info라는 변수가 있다. 이 때, Thread1이 null이 아님을 확인하고 값을 가져오려고 하는데 조건문을 통과하자마자 컨텍스트 스위치가 된다. 그런데 Thread2가 proc_info을 null로 바꿔버렸다. 다시 컨텍스트 스위칭이 되어 조건문에서 방어용으로 만들어놓은 저 null이 있었지만 fputs(thd->proc_info, ...)으로 thd->proc_info로 뭔가를 하려는 순간 null예외가 떠버릴 것이다. 이런 버그가 발생하지 않으려면 조건문 외부와 변경 부분에 Lock을 걸면 된다.

Thread1:
if(thd->proc_info){
	...
    fputs(thd->proc_info, ...);
    ...
 }
 
 Thread2:
 thd->proc_info = NULL;

 

 두 번째 예시는 Condition Valiable을 제대로 사용하지 않아서 발생하는 문제이다. 아래의 상황은 쓰레드가 생성이 되고 쓰레드에 있는 뭔가를 참조해야 하는데, 쓰레드를 만들기도 전에 참조를 하고 있다. 앞의 글에 있었던 방법처러 Lock과 condition Valiable을 통해 특정한 변수 하나를 만들어서 while문으로 잘 처리해주면 된다.

Thread1:
void init(){
	mThread = PR_CreateThread(mMain, ...);
 }
 
 Thread2:
 void mMain(...){
 	mState = mThread->State;
 }

[ 데드락 ]

 데드락은 앞에 봤던 예시들 중 생산자 소비자에서 Condition Valiable을 1개만 사용했을 때 일어난 모든 쓰레드가 잠들어 있는 상태와 같은 경우를 의미한다. 즉, 둘 이상의 쓰레드가 다른 프로세스가 점유하고 있는 자원을 서로 기다리다가 무한 대기하는 상황을 말한다.

 

 데드락은 아래의 4가지 조건을 모두 만족해야지 일어나는 버그이다.

 

Mutual Exclusion(상호 배제): 공유 자원에 접근할 때 본인만 자원을 가질 수 있는 상황으로 다른 쓰레드는 본인이 가진 자원을 놓기 전까지 접근할 수 없다.

Hold-and-wait(점유 대기): 본인이 가지고 있는 자원을 계속 들고 있으면서 다른 쓰레드가 가진 자원을 기다리고 있는 상황을 말한다.

No Preemption(비선점): 쓰레드의 자원을 강제로 제거가 불가능한 상황을 말한다.

Circular wait(순환 대기): 각 쓰레드가 서로 원하는 다른 자원을 가지고 있는 경우를 말한다.

 

 데드락은 4가지 조건을 모두 만족해야지 일어나는 버그이므로 하나라도 없애버리면 데드락은 일어나지 않는다. 그런데 Circular Wait, Hold-and-wait를 예방하는 방법은 락 순서를 보장하거나 한번에 락을 획득하게 프로그래밍을 해야 한다. 이걸 짜는게 쉽지 않다. Mutual Exclusion은 하드웨어의 도움을 받으면 해결할 수 있지만 방법이 제한적이기 때문에 좋지 않다. 

 

 가장 권장하는 방법은 No Preemption을 없애는 것이다. lock을 얻을 수 있는지 없는지 확인하고 lock을 획득하는 방법으로 trylock이라고도 한다. 그런데 이 방법이 간단하지만 문제는 서로 Lock을 획득하려고 시도를 하고 실패해서 trylock만 하는데 CPU자원을 쓸 수 있다. 이 때에는 sleep() 같은 함수로 약간의 딜레이만 주면 해결할 수 있어서 이 방법을 많이 쓰는 것 같다.

top:
    lock(L1);
    if(tryLock(L2) == -1){
    	unlock(L1);
        goto top;
    }

 

 아니면 OS 차원에서 공유자원 여부를 체크해서 같은 공유자원을 사용하는 쓰레드끼리는 같은 CPU가 실행하도록 하는 방법도 있는데 이는 OS를 만드는 사람의 역할이지 프로그래머가 할 수 있는 영역이 아니라 생략하도록 하겠다.

[ 참고 자료 ]

https://icksw.tistory.com/164

운영체제 - 서의성 강의