[시스템 소프트웨어]04-1 프로세스 동기화, 생산자/소비자 문제

yesman·2021년 12월 20일
0

시스템 소프트웨어

목록 보기
9/23

동기화의 필요성: 2개 이상의 프로세스들이 동시에 수행되고 있을 경우(멀티 프로그래밍), 이들은 경쟁관계에 있든지 혹은 공동의 목표를 위한 협동관계에 있든지 둘 중에 하나이다. 이때, 이들이 경쟁 혹은 협동관계는 이들이 수행하기 위해 필요한 공유자원에 대한 경쟁 혹은 협동을 의미하는 것이 보통이다.

예를 들어 실생활에서 협동관계가 나타난다고 하면, 학급청소를 예로 들 수 있다. 여러 사람이 교실을 깨끗하기 위해 각자 맡은 일을 수행해야하는데 바닥을 쓸기 전에 책상을 뒤로 밀어야 하고 물걸레를 하기 전에는 바닥을 쓸어야 한다. 이렇게 협동 관계에서 일을 수행할 때 순서를 정해서 해야한다. 이 경우는 하나의 일을 여러 개의 프로세스로 나누어 일을 진행하는 경우로 볼 수 있다.

또 실생활에서의 경쟁관계는 화장실 사용이 있다. 화장실을 사용하는 경우를 볼 수 있다. 공유자원(화장실 칸)은 적은데 이를 사용하고자 하는 사용자는 매우 많다. 그래서 화장실 사용을 위해 안에 사람이 있는지 확인한 후 앞에서 기다리고, 안에 있던 사람이 나오면 들어가서 사용한다. 이것은 모든 프로세스들이 공유하고 있는 자원에 대한 접근이 일어나고 있는 경우이다. 제한된 공유 자원을 여러 사용자가 동시에 사용하고자 하는 경우이다.

위의 예에서 발생하는 모든 문제들을 해결하는 방법을 Process syncronization, 프로세스 동기화라고 한다. 3가지 방법이 있다. 생산자/소비자 문제, 읽기/쓰기 문제, 식사하는 철학자 문제.

생산자/소비자

생산자/소비자 문제는 주어진 버퍼를 채우는 것이다. 여기서 버퍼는 생산자가 생성한 데이터의 개수로 소비자가 이 데이터의 개수를 읽어내서 출력한다. 정수 count를 사용하여 버퍼에 차있는 데이터의 개수를 센다.
처음에는 아무것도 없으므로 0으로 초기화한다. 새로운 데이터를 생산자가 생산하면 count를 1만큼 증가시킨다. 소비자가 데이터를 읽어내면 1만큼 감소시킨다.

코드로 다음과 같이 나타낼 수 있다.

Producer

while (true)
{	/* produce an item and put in nextProduced
	while (count == BUFFER_SIZE); // do nothing
	buffer[in] = nextProduced
	in = (in + 1) % BUFFER_SIZE;
	count++;
}

BUFFER_SIZE가 count와 같다면, 즉 버퍼가 가득 찼다면 아무것도 안한다.
buffer의 정수 in만큼의 인덱스에 생산한 데이터를 넣는다.
그리고 정수 in에 그 다음 데이터를 넣기 위해 in에 +1을 한 값에 BUFFER_SIZE를 나눈 것의 나머지 값을 넣는다. 모듈러 연산을 하는 이유는 버퍼가 circular 버퍼이기 때문이다.

Consumer

while (true)
{	while (count == 0); // do nothing
	nextConsumed = buffer[out];
	out = (out + 1) % BUFFER_SIZE;
	count--;
	/* consume the item in nextConsumed
}

count가 0이라면, 즉 버퍼에 아무것도 없다면, 읽을게 없다면 아무것도 하지 않는다.
다음 소비할 것은 buffer의 정수 out인덱스 만큼의 값이다.
정수 out은 out에 +1을 한 값에 BUFFER_SIZE로 모듈러 연산을 한 값이다.
count 값을 1만큼 줄인다.

Race Condition

producer와 consumer는 버퍼와 버퍼의 데이터, 데이터의 개수(count)를 공유중이다. 이 말은 프로세스 간의 공유자원이 있어서 원치 않은 결과를 내놓을 수 있다는 것이다.

위 코드의 count++은 이것을 뜻할 것이다.

register1 = count
register1 = register1 + 1
count = register1

count--도 마찬가지로 이것을 뜻할 것이다.

register2 = count
register2 = register2 - 1
count = register2

cpu에서는 count++과 count--를 이렇게 3단계 동작으로 수행한다. 이 동작은 서로 끼어들면 안된다. 하지만 다음과 같은 상황을 보면 원치 않는 결과가 나타난다.

count = 5로 초기화했다고 하자.
S0: producer가 register1 = count 를 수행한다.
S1: producer가 register1 = register1 + 1 를 수행한다. register1은 6이 된다.

__프로세스 스위치 발생

S2: consumer가 register2 = count 를 수행한다. 이때 producder에서 증가시킨 count의 값은 업데이트가 되지 않았으므로 여전히 count는 5이다.
S3: consumer가 register2 = register2 - 1 를 수행한다. register2는 4이다.

__프로세스 스위치 발생

S4: producer가 count = register1 를 수행한다. count는 6이 된다.

__프로세스 스위치 발생

S5: consumer가 count = register2 를 수행한다. 최종적으로 count에 값을 넣는 것은 consumer이므로 count는 4가 된다.

이렇게 두개의 동작이 서로 간섭하는 것(interleaving)을 race condition이라고 한다.
이 race condition이 일어나는 영역을 임계영역(Critical-Section)이라고 한다. 이 영역을 한 동작이 사용하고 있을 때 다른 한 동작이 간섭하지 않도록 해야한다.

profile
유니티

0개의 댓글