acquire() {
while (!available);
avilable = false;
}
release() {
avilable = true;
}
do {
acquire();
/* critical section */
release();
/* remainder section */
} while (true);
acquire()
과 release()
는 atomic 해야 한다.
-> hardware atomic instructions로 구현한다.
mutex lock은 busy waiting (spinlock) 이 발생한다.
busy waiting
sleep()
하고 나중에 다시 깨우는 방법Semaphore는 Mutex Lock보다 정교한 방법을 제공한다.
Semaphore S는 integer value이다. (<-> mutex lock의 available은 boolean)
두 개의 atomic한 operation이 있다.
wait()
과 signal()
또는 P()
와 V()
wait(S) {
while (S<=0); // busy wait
S--;
}
signal(S) {
S++;
}
binary semaphore
Counting semaphore
Semaphore 의 다른 예시: 순서 동기화
Binary semaphore: 초기값 S1 = 0
Task 1 code();
V(S1);
P(S1);
Task 2 code();
Implementation
wait()
과 signal()
을 동시에 실행할 수 없다는 것을 보장한다.wait()
과 signal()
코드 자체가 critical section에 놓인다.Implementation with no waiting
signal()
을 호출하면 대기 큐에 있던 프로세스를 깨우고 ready queue로 보내 CPU 자원 획득을 기다릴 수 있도록 한다.typedef struct
{
int value;
struct process* list; // linked list
} semaphore;
wait(semaphore* S)
{
S->value--;
if (S->value < 0)
{
add this process to S->list;
sleep(); // 대기 큐로 이동함.
}
}
signal(semaphore* S)
{
S->value++;
if (S->value <= 0)
{
remove a process P0 from S->list.
wakeup(P); // 준비 큐로 이동함.
}
}
-> 위 방법은 무한루프를 돌지 않으므로 single core에서 효율적으로 사용할 수 있다.
disable_interrupt()와 enable__interrupt() 방법은 멀티코어보다 싱글코어에서 효율적이다.
멀티코어의 경우, busy waiting이 더 낫다.
monitor monitor-name {
// shared variable declarations <- private 변수 선언
...
procedure P1 (...) {...}
...
procedure Pn (...) {...}
Initialization code (...) {...}
};
- queues associated with x,y conditions: 순서 제어 (order synchronization)
- entry queue: monitor로의 진입 관리, mutual exclusion 제어
condition x, y
: 순서 동기화를 위한 변수x.wait()
: x.signal()
전까지 blockx.signal()
: 만약 x.wait()
이 없으면 아무런 일도 하지 않는다.monitor ResourceAllocator {
boolean busy; // 공유 변수
condition x; // queue
void acquire(int time) { // = wait()
if (busy)
x.wait(time); // 바쁘므로 대기
busy = true;
}
void release() { // = signal()
busy = false;
x.signal();
}
initialization_code() {
busy = false; // available = true
}
};
acquire()
와 release()
구현만약 P가 x.signal()을 호출한 뒤에 추가적으로 해야 할 일이 있다면, 그 일을 먼저 하는 것과 다른 P가 wait 상태에서 깨어나 들어오는 것 중 어떤 게 먼저일까?
Signal and wait - 다른 P가 임계 영역으로 들어올 수 있도록 signal을 호출하고 wait 상태로 간다.
Signal and continue - 새로 깨어난 P는 임계 영역에 있던 P가 나머지 일을 끝낼 때까지 기다린다.
두 방법 모두 장단점이 존재하며, Signal 후 잠깐 물러난 프로세스가 기다리는 큐는 우선순위가 높다.
semaphore monitor_lock
// 초기값 = 1semaphore sig_lock
// 초기값 = 0int sig_lock_count = 0
wait(monitor_lock); // 모니터 진입 가능여부 판단
...
body of F
...
if (sig_lock_count) > 0) // signal 후 잠깐 wait 중인 프로세스가 있다면
signal(sig_lock); // 먼저 들어올 수 있다
else // 없다면
signal(monitor_lock); // 다음으로 모니터 밖에서 기다리는 프로세스가 들어온다
int x_count;
// 초기값 0, x를 기다리고 있는 프로세스의 수semaphore x_sem;
// 초기값 0/* x.wait */
x_count++;
// wait 하기 전에 들여보낼 process를 찾는다.
if (sig_lock_count > 0) // sig_wait이 존재한다면
signal(sig_lock); // 깨운다
else // 없으면
signal(monitor_lock); // 밖에서 기다리는 프로세스를 깨운다.
wait(x_sem); // wait 상태
x_count--; // wait 완료
/* x.signal */
if (x_count > 0) { // x_wait 하고 있는 프로세스가 있으면
sig_lock_count++; // 그 프로세스를 들여보내고 나는 sig_wait 할 것이다
signal(x_sem); // 기다리고 있는 프로세스 들여보낸다
wait(sig_lock); // sig_wait
sig_lock_count--; // wait 완료
}