본문 바로가기
운영체제

운영체제 8장 - 프로세스 관리(5) : 세마포(semaphore) -

by ChocoPeanut 2017. 4. 19.

운영체제 8

- 프로세스 관리(5) : 세마포(semaphore) -

 

프로세스/쓰레드 동기화는 여러 개의 프로세스/쓰레드가 공통으로 데이터에 접근하려고 할 때 발생하는 문제를 해결하는 방법이다. 동기화 부분은 스케줄링과 더불어 프로세스 관리에서 매우 중요한 부분을 차지한다. 세마포는 프로세스/쓰레드 동기화를 위한 도구 중 하나이다. 앞장에서 세마포의 원리에 대해서 알아보았는데 이번 장에서는 더욱 자세히 세마포에 대해 다루어보도록 하겠다.


쓰레드가 동작하는 공간에는 공통 데이터를 수정할 수 있는 임계구역이 존재한다. 쓰레드가 동작을 하면서 임계구역에 들어가게 되면 데이터를 수정할 수 있다. 쓰레드가 코드를 동작하는 도중에 acquire() 명령어가 실행되면 임계구역에 들어가라는 명령이므로 쓰레드가 임계구역에 들어간다. 임계구역에 있는 쓰레드는 release()라는 명령어가 실행되면 임계구역에서 나오게 된다. 이러한 명령어들은 쓰레드 안에 작성된 값이다.


여기서 문제가 발생할 수 있는 것은 하나의 쓰레드가 자신의 임계구역에 들어가서 공통으로 사용하는 데이터를 수정하고 있는 상황에서 문맥 전환(context switching)에 의해 다른 쓰레드가 자신의 임계구역에 가서 공통으로 사용하는 데이터를 수정하려고 시도할 때이다. 이렇게 되면 결과적으로 데이터의 수정이 이상하게 변동될 수가 있다. 이러한 문제를 임계구역 문제라고 한다.


위와 같은 문제가 발생하지 않게 하는 방법이 바로 상호배타(Multual exclusion)이다. 그러면 어떻게 상호배타를 만족시킬 수 있게 할 수 있는가? 이 때 사용하는 도구가 바로 세마포이다. 세마포는 정수 값을 가지는데 처음에 나타나는 정수는 임계구역에 들어갈 수 있는 쓰레드의 수를 나타낸다. 한 쓰레드가 acquire() 명령어를 실행해서 임계구역에 들어가게 되었을 때 문맥 전환에 의해 다른 쓰레드가 acquire() 명령어를 실행했다고 하더라도 임계구역에 들어갈 수 없게 막는 공간이 바로 세마포이다.



세마포이 동기화에서 적용되는 동작원리를 상세히 살펴보자. 

세마포는 쓰레드를 보관하는 Queue를 가진다. 그리고 앞에서 말했듯 정수 값을 가진다. 여기서는 시작하는 정수 값을 1로 놓고 설명을 하겠다. 임계구역에 접근 가능한 쓰레드가 하나라는 것을 의미한다. 임의의 쓰레드가 임계구역에 쓰레드가 존재하지 않을 경우 acquire()명령을 실행할 수 있다. 세마포는 acquire()명령을 받을 경우 정수 값을 1감소시킨다. 그리고 정수 값이 0보다 작을 경우 세마포의 Queue에 쓰레드를 block시킨다. 하지만 지금의 정수 값이 1이였는데 쓰레드가 acquire()명령을 실행한 경우 0이 되므로 세마포의 Queue에 갇히지 않고 바로 임계구역으로 이동하여 명령을 수행한다. 하지만 컴퓨터의 CPU 할당은 문맥 전환(context switching)에 의해 다른 쓰레드에게 갈수 있다. 따라서 다른 쓰레드가 문맥 전환에 의해 코드가 실행되게 되었다고 가정하자. 그런데 이 다른 쓰레드가 또 acquire()명령을 실행을 하게 되면 어떻게 될까? 두 번째 쓰레드도 임계구역에 들어가고 싶어 한다. 하지만 임계구역에는 이미 하나의 쓰레드가 존재하고 있으므로 임계구역에 들어가면 안 된다. 이를 제어하는 것이 바로 세마포이다. acquire()명령이 또 실행되었으므로 세마포의 정수 값이 또 1감소하게 된다. 그러면 정수 값은 1이 되어 0보다 작은 값을 가지게 된다. 이렇게 되면 두 번째 쓰레드는 세마포의 Queueblock을 당해 임계구역에 접근을 하지 못하는 상황이 된다. 세마포의 Queueblock을 당한 쓰레드는 혼자 힘으로 풀려나갈 수 없다. 그렇다면 어떻게 두 번째 쓰레드는 임계구역에 들어갈 수 있는가? 세마포에서 줄을 서서 잘 기다리고 있으면 다시 문맥 전환에 의해 앞에 먼저 임계구역에 들어간 쓰레드가 release()라는 명령을 수행할 수 있게 되고 이를 통해 임계구역에서 나가고 싶다는 의사를 표시한다. 그렇게 되면 세마포는 이를 인지하여 정수 값을 1증가시킨다. 그렇게 되면 정수 값이 0보다 크게 되므로 줄을 서고 있는 쓰레드가 wakeup을 통해 세마포에서 탈출을 하게 된다. 이렇게 탈출한 쓰레드는 임계구역에 접근을 허가받게 되어 들어가서 데이터를 수정할 수 있게 된다. 이런 방식으로 동시에 임계구역을 접근할 수 없게 하는 상호배타가 적용이 가능하게 된다.






세마포는 프로세스 실행 순서를 원하는 대로 제어하는 역할로도 사용할 수 있다

이를 Ordering이라고 한다. 예를 들어 설명해보자. 프로세스가 P1P2가 있다고 하자. 각각의 프로세스는 각자의 코드를 가지고 있다. P1S1이라는 내부 코드를 P2S2라는 내부 코드를 가지고 있다고 가장하자. 그런데 여기서 우리는 CPU 스케줄링과 상관없이 무조건 P1S1이 먼저 실행시키고 싶다고 하자. 그러면 어떻게 코드를 짜야할까? 우리는 세마포를 도구 사용하면 가능하다. 세마포는 정수 값과 프로세스 보관을 가지는 도구이다. 먼저 세마포의 정수 값에 0으로 놓는다. 초기 값은 허용되는 프로세스의 수이므로 0이면 아무도 허용이 되지 않는다는 것을 의미한다. 만약 P1이 먼저 실행되면 S1이 먼저 실행이 되므로 우리는 아무런 효과를 주지 않아도 된다. 하지만 만약 P2CPU 스케줄링에 의해 먼저 실행되게 된다면 우리가 의도한 대로 실행이 되지 않는 것이다. 그래서 우리는 임의로 P2의 실행코드 앞에 acquire()명령을 넣어준다. acquire()명령을 실행하면 세마포의 정수 값이 1감소하게 되어 1이 된다. 그렇게 되면 세마포의 정수 값이 0보다 작은 값을 가지게 되므로 P2는 세마포의 프로세스 보관 Queueblock 당하게 된다. 그러면 뒤의 코드를 실행할 수 없는 상황에 생긴다. 그렇게 되면 어떻게 되든 문맥전환에 의해 P1이 먼저 실행하게 된다. 그런데 P2를 영원히 갇혀있게 하면 안 된다. 따라서 P1의 내부코드 끝부분에 release()명령을 넣는다. 그렇게 되면 세마포의 정수 값이 1증가하게 되어 다시 0이 된다. 그러면 세마포에 저장된 P2wakeup이 되어 세마포에서 빠져나오게 된다. 이렇게 세마포를 이용한 명령을 임의로 입력해 넣어 우리가 원하는 프로세스의 처리 순서를 제어할 수 있다.