Semaphore
공유자원의 개수가 여러개 있을때, 그 수만큼의 thread만이 임계영역에 진입하여 있을 수 있도록 제한하는 방식이다.
현재 사용 가능한 공유자원의 개수는 int x와 같은 정수형 변수로 관리하는데 이 변수 x를 특별히 Semaphore라 부른다.
Semaphore는 또한 Smaphore를 사용해서 공유자원문제를 해결하는 방법에 대한 의미를 포함하기도 한다.
⊗ 공유자원의 개수가 1개일때 Binary Semaphore라 하며, 2개 이상인 경우에 Count Semaphore라 한다.
1. Semaphore 동작방식
♦ Thread는 Semaphore 값이 0 보다 큰 경우에만 임계영역으로 진입 할 수 있다.
♦ 임계영역에 진입할 수 있는 경우에는 먼저 현재의 Semaphore 값을 1 감소시킨다.
♦ 공유자원을 Access하고, 임계영역을 빠져 나올 때에는 Semaphore 값을 1 증가 시킨다. → 이를 Signalling이라 한다.
♦ 임계영역에 있지 않은 Thread도 Signaling은 줄 수 있다.
⊗ Semaphore 값을 변결할 때에는 최대한 빠르게(atomic operation) 변경할 수 있도록 한다.
2. Semaphore 구현초안
세마포어의 구현도 뮤텍스에서와 마찬가지로 Busy-Waiting 방식으로 가장 단순하게 구현되어 질 수 있다.
아래에서 변수 count가 Semaphore 이다. 즉 현재 임계영역에 입장 가능한 쓰레드의 수를 나타내고 있으며,
Thread는 count>0일 때만 임계영역으로 진입할 수 있으다.
global int count=3; | // 공유자원 최대개수=3, 즉 count=0 진입불가 신호, count>0 진입가능 신호 |
공유자원을 사용하는 momo thread 구현 | 공유자원을 사용하는 momo thread 구현 |
void momo(void ) { while(count>0) count–;
momo의 공유자원 액세스(임계영역); count++;
기타 다른 할일들 실행; } |
void toto(void ) { while(count>0) count–; //세마포어 wait 구현 // 공유자원의 사용권이 1장 이상일 때 까지 무한 대기한다. toto의 공유자원 액세스(임계영역); count=count++; //세마포어 signal 구현 // 공유자원의 사용권을 1 중가시켜, 다른 쓰레드가 임계영역에 진입할 수 있도록 신호를 보낸다. 기타 다른 할일들 실행; } |
3. Semaphore 구현보완
Busy-Waiting 방식에서는 모든 자원이 사용 중일 때 새로 자원을 사용하려는 Thread는 그 자리에서 계속 뺑뺑이만 돌고 있을 것이다.
RTOS는 시간이 금인데 하는 일 없이 뺑뺑이만 돌고 있는 Thread에게도 시간을 할당 해주어야 하니 미칠 노릇이다.
따라서 RTOS 시스템에서는, 시스템마다 차이는 있겠지만, 다음과 같은 기능을 추가하여 Busy Waiting 방식을 보완하여 구현되어 있다.
기능추가 | 기능설명 |
type struct{ int count; struct thread *waiting_queue; }sem; |
기존 Count(semaphore)를 구조체 sem(semaphore)으로 확장해서, →Thread 리스트를 기록할 Queue 를 추가하였다.
|
wait(sem){ sem.count–; if(sem.count<0) block(); } |
기존 wait에 thread가 뺑뺑이 돌 상황이라면 block/sleep/suspend 상태로 천이 될 수 있도록 기능을 추가한다. → 뺑뺑이 돌 상황의 쓰레드는 자신을 block 상태로 천이 시킨다, 해당 thread는 waiting_queue에 넣어진다. |
signal(sem){ sem.count++; if(sem.count<=0) wakeup(); } |
기존 signal에 wakeup()을 추가하였다. → 대기상태의 Thread 중 하나를 Ready 상태로 복귀시킨다. 해당 thread는 waiting_queue에서 빠져나온다. |
Pseudo 코드구현
다음과 같이 구현하여 Thread가 공유자원을 사용할 수 없을 때 무작정 기다리지 않고 대기상태로 빠지도록 만든다.
결과로, 실시간적으로 일을 처리 해야하는 다른 Thread들이 일을 처리할 수 있는 기회(시간)을 더 많이 할당 받을 수 있게 될 것이다.
공유자원을 access 하는 momo thread | 공유자원을 access 하는 toto thread |
void momo(void ) { wait(sem);
momo 쓰레드의 공유자원 액세스(임계영역);
signal(sem); 기타 다은 할일들 처리; } |
void toto(void ) { wait(sem);
toto 쓰레드의 공유자원 액세스(임계영역);
signal(sem); 기타 다른 할일들 처리; } |