Process Synchronization이란
여러 프로세스들이 동작중일 때 그들을 동기화하는 것을 의미한다.
과거에는 하나의 디스켓당 하나의 프로그램을 실행하는 방식이었으나 현재는 하나의 OS에 복수의 프로세스가 실행되므로 서로간의 간섭을 대비하는 장치들이 필요하다.
생산자-소비자 문제 (producer-consumer)
공유 데이터에 두 프로세스가 동시에 접근했을 때 발생하는 간섭의 대표적인 예시이다.
Producer
while (true) {
while (counter == BUFFER_SIZE);
/* 버퍼가 꽉 차면 아무것도 하지 않음 */
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE; // 인덱스가 버퍼 사이즈를 벗어나면 0 으로 재설정
counter++;
}
Consumer
while (true) {
while (counter == 0);
/* 큐가 비어있으면 아무것도 하지 않음 */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE; // 인덱스가 버퍼 사이즈를 초과하면 0 으로 재설정
counter--;
}
위에서 Producer, Consumer 모두 counter변수에 접근하고 있다.
먼저 counter++, counter--가 동작하는 방식을 살펴보면 아래와 같다.
counter++ | counter-- |
register1 = counter register1 = register1 + 1 counter = register1 |
register2 = counter register2 = register2 - 1 counter = register2 |
counter++, counter--는 각각 한 줄의 코드이지만 CPU는 이것을 하나의 연산으로 처리할 방법이 없기 때문에, 다른 변수에 일시적으로 대입하여 3번의 연산으로 처리한다.
만약 위 작업이 동시에 실행되어 counter++, counter--를 순서대로 처리하던 도중, counter++ 프로세스가 종료 또는 지체된다면 counter--의 연산 결과에 영향을 주게된다.
// counter가 5에서 시작한다고 가정
// Producer의 연산
register1 = counter; // register1 = 5
register1 = register1 + 1; // register1 = 6
// Consumer의 연산
register2 = counter; //register2 = 5
register2 = register2 - 1 //register2 = 4
// Producer의 연산
counter = register1; // counter = 6
// Consumer의 연산
counter = register2; //counter = 4;
Producer의 연산 도중 Consumer의 연산이 실행되어 간섭이 일어난 예시이다.
Producer의 연산 2문장이 실행된 후 Context switch가 발생하여 최종적으로 counter의 값은 6이 아닌 4가 된다.
이러한 상황을 두 프로세스간의 경쟁 상태(Race Condition)라고 부른다.
이를 방지하기 위해 test_and_set()을 이용할 수 있다.
test and set (검사와 지정)
상호배제를 구현하는 간단한 방법이다.
do {
while (test_and_set(&lock))
; /* lock = true 이면 아무것도 하지 않음 */
/* 임계구역 critical section */
lock = false; // 종료된 후 lock을 풀어줌
} while (true) ;
boolean test_and_set (boolean *target) {
boolean rv = *target;
*target = TRUE; // lock 값이 무엇이든 TRUE로 설정
return rv;
}
첫 번째 프로세스(P1)가 시작될 때 lock값을 true로 설정한다.
두 번째 프로세스(P2)가 진입하더라도, lock값은 true이므로 while문을 빠져나가지 못한다.
P1이 임계구역을 마친 후 lock을 false로 설정하면, P2가 while문을 빠져나와 임계구역 작업을 진행한다.
세마포어 Semaphore
Integer변수로 작업대기를 구분하는 방법이다.
// S = 1 로 시작한다고 가정
wait (S) {
while (S <= 0)
; // 작업 대기
S--;
}
// 각 프로세스 완료 후 호출
signal (S) {
S++;
}
S값이 최초 1이므로 P1은 while문을 통과하며 S값은 0이 된다.
P2가 실행되면 S값은 0이므로 while문을 통과하지 못하고 계류한다.
P1이 완료되면 signal함수를 호출하여 S값은 1로 변경되며, P2는 while문을 통과하여 진행된다.
0과 1값을 이용한 이진 세마포어 방식이며, S값을 다양한 방식으로 이용할 수 있는데 이 경우 계수형 세마포어 (Counting Semaphore)라 칭한다.
이를 응용하면 특정 프로세스가 다른 프로세스보다 반드시 먼저 실행되도록 만들 수 있다.
아래의 경우 P1이 반드시 P2보다 먼저 실행된다.
// synch = 0 로 시작한다고 가정
P1:
S1;
signal (synch);
P2:
wait (synch);
S2;
P2가 먼저 실행되더라도 synch값은 0이므로 wait를 빠져나오지 못한다.
이후 P1이 실행되면 S1의 종료 후에 synch값을 1로 설정하고, P2가 wait을 빠져나와 S2가 실행된다.
'운영체제' 카테고리의 다른 글
[운영체제] CPU Scheduling (0) | 2021.06.19 |
---|
댓글