본문 바로가기
  • 게임 개발과 프로그래밍 그리고 인공지능
알고리즘 및 자료 관리/컴퓨터 구조 & 운영체제

프로세스 동기화 2 - 동기화 기법

by huenuri 2024. 10. 17.

동기화의 개념을 익혔으니 이제 동기화 기법에 대해서 공부해보려고 한다. 이제 새벽 3시가 다 되어간다. 오늘은 철야를 하며 공부해 볼 생각이다.
프로세스를 동기화하지 않으면 겉보기에 아무런 문제 없어보이는 코드도 예기치 못하게 작동될 수 있다. 이번 절에서는 동기화를 위한 대표적인 도구인 뮤텍스 락, 세마포, 모니터에 대해 학습해 보겠다.
 

프로세스 동기화는 어떻게 이루어질까? 어떻게 해야 임계 구역에 오직 하나의 프로세스만 진입하게 하고, 올바른 실행 순서를 보장할 수 있을까? 이제 동기화를 위한 대표적인 도구인 뮤텍스 락, 세마포, 모니터에 대해 알아볼 것이다.


 
 
 

뮤텍스락

임계 구역 문제와 이를 해결하기 위한 동기화를 옷 가게에서 탈의식을 이용하는 것에 비유해 보자. 옷 가게에서 마음에 드는 옷이 없으면 손님은 탈의실에 들어가서 옷을 입어볼 수 있다. 이때 탈의실에는 한 명의 인원만 들어갈 수 있다. 손님은 탈의실이라는 자원을 이용하고 탈의실 안에는 손님 한 명 씩만 들어올 수 있으니, 손님은 '프로세스', 탈의실은 '임계 구역'이라고 할 수 있다.

만약 밖에서 탈의실에 사람이 있는지 없는지 알 수 없는 상황이라면 어떻게 탈의실이 이용 중임을 알 수 있을까? 일단 탈의실을 열어 보고 자물쇠가 걸려 있다면 탈의실 안에 사람이 있다고 판단하고 기다린다. 자물쇠가 걸려 있지 많으면 탈의실을 이용하면 되는 것이다.
임계 구역이 진입하는 프로세스는 '내가 지금 임계 구역에 있음'을 알리기 위해 뮤텍스 락을 이용해 임계 구역에 자물쇠를 걸어둘 수 있고, 다른 프로세스는 임계 구역이 잠겨 있다면 기다리고, 잠겨 있지 않다면 임계 구역에 진입할 수 있다.
 

acquire 함수는 프로세스가 임계 구역에 진입하기 전에 호출하는 함수이다. 만일 임계 구역이 잠겨 있다면 임계 구역이 열릴 때까지(lock이 false가 될 때까지) 임계 구역을 반복적으로 확인하고, 임계 구역이 열려 있다면 임계 구역을 잠그는(lock을 true로 바꾸는) 함수이다.
release 함수는 임계 구역에서의 작업이 끝나고 호출하는 함수이다. 현재 잠긴 임계 구역을 열어주는(lock을 false로 바꾸는) 함수라고 보면 된다.
 

 
 
acquire와 release 함수를 아래와 같이 임계 구역 전후로 호출함으로써 하나의 프로세스만 임계 구역에 진입할 수 있다.

 
 

 
앞서 언급한 생산자 소비자 문제 또한 아래와 같이 뮤텍스로 구현할 수 있다.

 
이렇게 되면 프로세스는

  • 락을 획득할 수 없다면(임계 구역에 진입할 수 없다면) 무작정 기다리고
  • 락을 획득할 수 있다면(임계 구역에 진입할 수 있다면) 임계 구역을 잠근 뒤 임계 구역에서의 작업을 진행하고,
  • 임계 구역을 빠져나올 때에 다시 임계 구역의 잠금을 해제함으로써

임계 구역을 보호할 수 있다.
 
 
acquire 함수를 다시 보면 임계 구역이 잠겨 있을 경우 프로세스는 반복적으로 lock을 확인하는 것을 알 수 있다.

이는 탈의실 문이 잠겨 있는지 쉴 새 없이 반복하며 확인해 보는 것과 같다. 이런 대기 방식을 바쁜 대기라고 한다.
 
C/C++, Python 등의 일부 프로그래밍 언어에서는 사용자가 직접 acquire, release 함수를 구현하지 않도록 뮤텍스 락 기능을 제공한다.


 
 
 
 

세마포

뮤텍스 락은 하나의 공유 자원에 접근하는 프로세스를 상정한 방식이다. 탈의실이 하나 있는 경우를 가정하고 만든 동기화 도구이다. 하지만 탈의실이 여러 개 있는 상황처럼 공유 자원이 여러 개 있을 경우(각 공유 자원에는 하나의 프로세스만 진입이 가능할지라도) 여러 개의 프로세스가 각각 공유 자원에 접근이 가능해야 한다.
 
예를 들어 옷가게에 탈의실이 세 개 있다고 생각해 보자. 여전히 하나의 탈의실에는 한 사람만 들어갈 수 있지만, 이 경우에는 세 명이 동시에 탈의실을 이용할 수 있다. 또 한 번에 하나의 프로세스만 이용할 수 있는 프린터 세 대가 있는 상황을 보겠다. 하나의 프린터를 사용할 수 있는 프로세스는 하나이지만, 총 세 개의 프로세스가 공유 자원(세 대의 프린터)을 이용할 수 있다.

엄밀히 말하면 세마포의 종류에도 아진 세마포와 카운팅 세마포가 있다.
 
 
 
세마포는 아래 그림과 같은 철도 신호기에서 유래한 단어이다. 기차는 신호기가 내려가 있을 때는 '멈춤' 신호로 간주하고 잠시 멈춘다. 반대로 신호기가 올라와 있을 때는 '가도 좋다'는 신호로 간주하고 다시 움직이기 시작한다. 

 
 

뮤텍스 락을 사용할 때 임계 구역 진입 전후로 acquire()와 release()를 호출했듯이 세마포도 임계 구역 진입전후로 wait()와 signal()을 호출한다.
 
 
 

 
 

 
 

 
 
 

 
 

 
 

 
 

뮤텍스 락에도 해당되는 문제인데, 사용할 수 있는 공유 자원이 없는 경우 프로세스는 무작정 무한히 반복하며 S를 확인해야 한다. 이는 마치 탈의실 문이 잠겨 있는지 아닌지 계속 반복해서 확인하는 것과 같다. 이렇게 바쁜 대기를 반복하며 확인할 시간에 CPU는 더 생산성 있는 작업을 할 수 있을 텐데, CPU 주기를 낭비한다는 점에서 손해이다.
 
실제로 세마포는 다른 더 좋은 방법을 사용한다. wait 함수는 만일 사용할 수 있는 자원이 없을 경우 해당 프로세스 상태를 대기 상태로 만들고, 그 프로세스의 PCB를 세마포를 위한 대기 큐에 집어넣는다. 그리고 다른 프로세스가 임계 구역에서의 작업이 끝나고 signal 함수를 호출하면 signal 함수는 대기 중에 프로세스를 대기 큐에서 제거하고, 프로세스 상태를 준비 상태로 변경한 뒤 준비 큐로 옮겨준다.
 
 

 
 
 
 

이 경우 P1이 먼저 실행되면 P1이 임계 구역에 먼저 진입하고, P2가 먼저 실행되더라도 P2는 wait 함수를 만나므로 P1이 임계 구역에 진입한다. 그리고 P1이 임계 구역의 실행을 끝내고 signal을 호출하면 그제서야 P2가 임계 구역에 진입한다. 즉, P1이 먼저 실행되든 P2가 먼저 실행되든 반드시 P2, P2 순서대로 실행된다.


 
 
 

모니터

세마포는 그 자체로 매우 훌륭한 프로세스 동기화 도구이지만, 사용하기가 조금 불편한 면이 이 ㅆ다. 

 
 

모니터는 공유 자원과 공유 자원에 접근하기 위한 인터페이스(통로)를 묶어 관리한다. 그리고 프로세스는 반드시 인터페이스를 통해서만 공유 자원에 접근하도록 한다.
 

 
 
 
 
이 밖에도 모니터는 세마포와 마찬가지로 실행 순서 제어를 위한 동기화도 제공한다.

 
 
 
 

 
여기서 헷갈리면 안 되는 점은 모니터에 진입하기 위해 삽입되는 큐(상호 배제를 위한 큐)와 wait가 호출되어 실행이 중단된 프로세스들이 삽입되는 큐(조건 변수에 대한 큐)는 다르다는 점이다. 전자는 모니터에 한 번에 하나의 프로세스만 진입하도록 하기 위해 만들어진 큐이고, 후자는 모니터에 이미 진입한 프로세스의 실행 조건이 만족될 때까지 잠시 실행이 중단되어 기다리기 위해 만들어진 큐이다.
 
모니터에 진입한 어떤 프로세스가 x.wait()를 통해 조건 변수 x에 대한 wait를 호출했다고 가정해 보겠다. 그 프로세스는 다음 그림처럼 조건 변수 x에 대한 큐에 삽입되므로 모니터는 다시 비게 된다. 그렇기에 다른 프로세스가 모니터 안에 들어올 수 있는 것이다.

 
 
 

 

 

동기화 관련 추가 학습

self-learning-cs/synchronization/syncronization.md at main · kangtegong/self-learning-cs

『혼자 공부하는 컴퓨터구조 & 운영체제』 (한빛미디어) 심화자료. Contribute to kangtegong/self-learning-cs development by creating an account on GitHub.

github.com

 


 
 
 
 

단원 마무리하기

 

 
세마포를 이용하면 반드시 바쁜 대기를 할 필요는 없고 대기 상태로 접어들게 할 수 있다. 스레드 B가 조건 변수 y에 대해 대기 상태에 있으므로 y.signal을 호출하면 스레드 B가 실행된다.
3번만 틀렸는데 이건 순서를 바꿔서 썼다. 이 부분이 많이 헤갈렸다. 동시에 실행되는 프로세스 혹은 스레드 간에는 상호 배제를 위한 동기화이고, 실행 순서 제어를 위한 동기화를 할 수 있다.
 


 
 
 

학습을 마치고

동기화 기법 이번 단원은 솔직히 정말 지루했다. 그래도 끝까지 공부를 잘 마쳤다. 나중에 이 기법은 복습을 해야 할 것 같다. 공부를 하면서도 이게 무슨 말인가 하는 것들이 참 많았다.
이제 13장까지만 마치면 오늘 새벽에 하려던 목표는 거의 완수된다.