본문 바로가기

cs/운영체제

14강 Process Synchronization 3

https://core.ewha.ac.kr/publicview/C0101020140408134626290222?vmode=f 

 

반효경 [운영체제] 14. Process Synchronization 3

설명이 없습니다.

core.ewha.ac.kr

 

전 강의 정리~~~

세마포어 변수값 1이라면

자원에 lock을 거는데 활용할수 있다.(mutex 용도)

변수값이 5,10 이러면 남아있는 자원의 갯수를 세는 용도가 된다.

 

 

synchronization 관련하여 3가지의 고전적인 문제를 살펴볼 것이다.

 

1.Bounded Buffer Problem

bounded buffer(buffer의 크기가 유한한 상태)환경에서 producer-consumer problem(생산자 소비자 문제) 

buffer : 임시로 데이터를 저장하는 공간

 

-> 문제가 무엇인가:

프로세스가 2가지 종류가 있다 생산자 프로세스, 소비자 프로세스 이다

생산자가 여러개가 있고 소비자도 여러개가 있다.

이런 상황에서 생산자는 공유 버퍼에 데이터를 만들어서 넣는 역할을 한다.(그림에서 주황색이 데이터가 찬 버퍼)(회색이 내용이 없는 비어있는 버퍼)

주황색은 생산자가 정보를 만들어서 넣어놓은 버퍼

회색은 애초부터 비어있거나 소비자가 데이터를 꺼내간경우

 

여기서 Synchronization 관련해서 벌어지는 문제

1. 기존의 문제처럼 동시에 하나의 버퍼에 접근하는경우 (여러 프로세스가)

-> 공유 버퍼이기 때문에 생산자가 동시에 도착해서 비어있는 버퍼 한곳에 동시에 데이터를 만들어서 넣는 경우(둘중하나는 덮어씌워짐)

데이터를 집어넣을때 버퍼에 락을걸어서 다른 프로세스의 접근을 막고 해야한다.

-> 소비자도 동시에 하나의 버퍼에 접근해서 하나를 동시에 같이 꺼내가버린 경우 문제가 생김

이또한 락을 걸어서 프로세스의 접근을 막아야한다.

 

2.버퍼가 유한하기 떄문에 생기는 문제

생산자가 한꺼번에 와서 buffer를 가득 채워버렸다.

->소비자는 안오고 생산자가 와서 정보를 넣고 싶은상황이면

buffer는 다찼고 생산자는 도착했고 -> 생산자 입장에서 사용할수 있는 자원이 없는 것으로 볼수있음

세마포어를 자원의 갯수를 세는 용도로 사용할 수 있다고 했는데 이런데 적용가능

버퍼의 갯수가 N개 이면 생산자가 버퍼 n개를 가득채우고 나면 생산자가 도착하더라도 정보를 채워넣을 빈 버퍼가 없는 상황이 생기고 소비자가 정보를 꺼내가야 빈 버퍼가 생기고 그제서야 데이터를 만들어서 넣을수 있다.

이상황에서 생산자 입장에서 자원은 빈 버퍼의 갯수이다.(카운팅해야할 자원)

빈버퍼를 획득하고 정보를 생성한다.

 

소비자 입장에서는 반대로 자원이 내용이 들어있는 버퍼이고 그것을 자원으로 관리해야한다.

생산자 프로세스가 자원을 만들어서 넣어줄때까지 기다려야한다.

 

이러한 생산자 소비자 문제에서 세마포어로 관리해야할 문제는 두가지이다.

 

1. 프로세스 여러개가 동시에 공유버퍼에 접근하지 못하도록 공유버퍼에 사용전 lock를 걸어서 혼자 베타적으로 접근할수있도록 하는것

2. 버퍼가 가득 차거나 아예 비었을때 가용자원의 갯수를 세는용도로 counting 용도로 세마포어를 사용하게 됨

 

생산자가 해야할일들의 목록이다 봐보자

 

1.빈버퍼가 있는지 보고 없다면 기다린다.

2.빈버퍼가 생기면 공유 버퍼의 lock 를 걸어서 다른 프로세스의 접근을 막는다.

3. 빈버퍼에 데이터를 넣는다.

4.lock를  푼다.

5. 내용이 들어있는 버퍼의 갯수를 1증가시킨다(소비자 입장의 자원)

 

소비자 입장에서 할일

생산자랑 반대라 생각하면된다.

 

이상황에서 공유 데이터

-> 버퍼 자체, 버퍼 조작변수(공유데이터 비어있는 위치를 나타내는 포인터 위치 같은것)

이런것 모든 프로세스가 접근할수 있기에 접근할때 lock 를 걸고 풀고 하는 과정이 필요하다.

 

생산자 소비자문제의 세마포어를 이용한 부분의 수도코드

 

우선 세마포어 변수를 3개를 두고있다

lock를 걸기위한 mutex(1임)

내용이 들어있는 버퍼를 세기위한 변수 full

비어있는 버퍼를세기위한 변수 empty

 

최초에 비어있는 버퍼의수 최고n개 내용 들어있는 버퍼의 수 0개

 

 

생산자 프로세스를 살펴보면

일단 아이템 x를 만드는 코드가 있다. -> 공유버퍼의 집어넣으려함

일단 빈버퍼를 확인하는 세마포어를 p함수를 실행

다음 빈게있으면 획득하고

빈버퍼가 없다면 대기함

다음 공유 자원인 버퍼에 접근할 것이기 때문에 P연산을 통해서 버퍼전체 에다가에다가 lock 를 걸어버림(mutex 자원의 획득)

다음 정보 집어넣고

V함수를 통해서 lock 를 풀어준다.

다음 V연산으로 또 자원이 들어있는 세마포어의 변수양을 늘려줌(소비자 입장의 자원을 늘려줌)

 

소비자의 경우 반대로 구성되어있다.

일단 차있는 버퍼가있는지 확인하고 세마포어의 P함수를 통해서 자원이있는지 확인 없다면 대기하고

그후 공유버퍼의 접근을 할것이니 mutex로 lock를 걸고

buffer에 있는 내용을 빼고난후

V함수로 lock를 풀어주고 

다음 v함수로 비어있는 버퍼의 수를 하나 늘려준다(생산자 입장의 자원)

다음 꺼내온 내용을 사용한다.

 

2.reader writer problem

reader writer 문제에는 프로세스가 두가지 종류가 있다.

읽는 프로세스 쓰는 프로세스 그리고 공유데이터를 DB라고 명명한다.

reader와 writer는 여러개가 있다.

 

DB를 쓰다보면 읽는 프로세스 쓰는 프로세스 lock 를 거는 문제가 당연히 일어나니 DB에서 일어나는 고전적인 문제를 보는것이다.

 

이러한 상황에서는 어떠한 문제가 발생하는가?

공유데이터인 DB에 여러 프로세스가 동시에 접근하면 안된다.

lock를 걸어서 하나의 프로세스만 접근하게 해주고 접근이 끝났다면 lock를 풀어주고 이런 작업을 해야 할것이다.

 

reader writer 문제에는 약간 다른점이있는데

writer는 동시에 여럿이 하면 안되지만

reader는 동시에 일어가도 동기화 측면에서 문제가 생기지 않는다.

 

쉽게 접근하자면 어떤 프로세스든 그냥 접근하면 db에 lock를 걸어버리고 나올때 풀어주면 되지만 이렇게하면 효율이 떨어질것이다.

 

그래서 writer가 접근한 경우에는 lock를 걸어줘야 하겠지만 reader의 경우에는 같이 읽을수 있게 해주도록 해야한다.

(read 도중에 writer 가 접근한다면 막아줘야함)

write는 독점적으로 해야한다(아무도 없을때만 해줄수있다.)

 

 

수도 코드를 보며 문제를 어떻게 해결해 나가는지 봐보자

 DB(대문자)가 공유데이터이다. -> 이에 대한 lock 용도로 거는 세마포어 변수가 db(소문자)이다.

 

writer 부터 보면

시작하자마자 P연산을 통해서 자원을 얻어서 lock를 걸어버리고 작업을 한다.

누군가 read든 write든 하고있다면 P연산에서 자원을 얻지못하고 걸려서 대기를 할것이다.

그리고 작업을 다마치고 난다음에는 V연산을 통해서 lock 를 풀어주는 작업을 하게된다.

(일반적으로 lock 를 거는것과 같은 작업으로 보면된다)

reader부분을 보면

쉽게 구현하려면 writer 와 똑같이 구현해도 문제없이 작동은 하겠지만 문제는 효율성이 떨어지게된다.

읽는 작업은 여럿이 동시에 해도 되기 때문이다.

 

근데 한가지 보면 일단 읽기 전에 lock을 걸긴 걸어야한다. 안그러면 읽고있는데 writer가 밀고들어올수 있기 때문이다.

읽을때도 lock 을 걸긴하지만 읽을려고 lock 를 걸은거라면 다른 writer들은 접근할수 있도록 해줘야한다.

 

그래서 readcount 라는 공유변수를 둔다.

reader가 몇명이 현재 db에 접근하고있는지에 대해 나타내는 변수이다.

 

그래서 내가 최초의 reader이라면 -> readcount 조건이 1이라면

db변수에 p연산을 해서 lock을 걸고

reader가 왔을때 만약 이미 누가읽고있었다면 readcount가 2이상이므로 lock 도 안걸리고 걸필요도 없다.

readcount 변수도 공유 변수이기 때문에 그냥 증가시키면 문제가 생길수있다 readcount 에 동시에 두 reader가 접근할수 있으므로 그래서 readcount 에 lock 걸기위해서 mutex변수를 둔다 

 

그래서 전체적인 과정을 보자면 readcount 에 접근하기위해 뮤텍스에 P연산을 해서 자원을 얻고난후 readcount 에 ++을 해주고

readcount 변수를 검사해서 db자체에 lock 을 걸지말지 결정후 readcount 자원을 V연산을 통해서 놓는다.

 

이렇게하면 여러 reader가 동시에 접근이 가능하다.

 

DB를 읽는 작업이 끝났으면 순차적으로 빠저나가면 되는데 내가 읽고있던 마지막 프로세스라면 == readcount 가 0이라면

DB 자체에 걸리는 lock 를 풀어주는 형식으로 해결한다.

 

 

그래서 결론을 보자면

 

공유데이터는 DB 자체와 readcount 라는 공유 변수이다.

 

그리고 세마포어 변수는 readcount 를 관리하는 mutex와 DB 자체 lock 를 거는 변수인 db가 있다.

 

 

이코드를 살펴보면 한가지 문제점이 있는데 

reader가 자리를 선점한후 순차적으로 계속온다면 writer는 중간에 와서 reader가 시간차로 공격하기 때문에 starvation 이 올수있다.

writer가 너무 오래기다리게 되는 경우가 위의 코드라면 생길수도 있다.

 

이런것은 큐에서 우선순위 aging 같은 방식으로 해결하면 되는것이다.(여기서 막 코드를 자세히 설명하고 그러지는 않는다.

-> 현실에서도 교통정리 하는사람이 있듯이 교통정리 코드를 따로 마련해야한다(여기서 살펴보지는 않는다 기존에 스케쥴링에서 많이봤으니 그런식의 코드가 필요하긴 하다는뜻)

 

식사하는 철학자 문제 

철학자 5명이 원탁에 있는데 두가지 업무가 있다 생각하기와 밥먹기 근데 철학자들이 각각 밥먹고 생각하는 주기가 다르다.

배가 고파지만 왼쪽 오른쪽에 있는 젓가락을 집어서 밥을 먹게되는데 밥먹고 나면 젓가락을 놓고 생각을 하게 되는 시스템이다.

밥먹으려면 젓가락을 양쪽을 다 쥐어야 한다는 조건이다.(한짝만 잡으면 못먹는다)

 

그래서 철학자 i명이 있다고 보고 (대충 5명정도라 치자)

 

코드보면 p연산이 이제 젓가락 잡는것이다.

 

각각의 세마포어 변수인 젓가락은 1로 초기화 되어있다(뮤텍스라는것)

 

위의 자료에서 식사하는 철학자 문제를 코딩해놓은것이다 근데 이 코드는 상당히 위험한 문제점이있다.

 

문제점:

1. 데드락의 가능성이 있다. -> 각 철학자들이 하나의 젓가락을 쥐고 안놓는경우 (동시에 각자 왼쪽 젓가락을 잡고있는경우)

 

-> 데드락을 막는 방법

젓가락을 동시에 집을수있는 경우에만 젓가락을 잡을수있는 권한을 준다.

비대칭 : 짝수 철학

자는 왼쪽부터 집게하고 홀수 철학자는 오른쪽 부터 집게하면 하나의 젓가락이 우선순위가 높기 때문에 하나를 잡기전에는 다른것들 잡을 생각을 안해서 해결할수있다. 이제 서로 사이에있는 젓가락을 잡았을 때만 나머지 젓가락을 잡을수있도록 하는것이다.

 

 

 

이게 젓가락을 두개 동시에 잡을수 있을때만 잡도록 한 해결책의 코드이다.

이게 그리 잘짠 코드는 아니란다 그래도 일단 보자(이거 모니터라는 추후에 나오는 개념 코드를 바꾼거라 세마포어의 철학에는 맞지않는 코드라함)

 

철학자 코드를 보면 

젓가락을 줍는 pick up

먹는 eat

젓가락을 놓는 put down

생각하는 think

이렇게를 계속 반복한다.

 

일단 철학자 상태는 생각하는거 배고픈거 먹는거 이렇게 3개 이넘으로 정의되어있다

 

세마포어 변수 5개가 있다. -> 5개는 철학자가 동시에 젓가락을 잡을 수 있는지 없는지를 따져서 그에 대한 권한을 표시하는 변수이다.

-> 세마포어 변수 1 이면 그 철학자가 양쪽다 젓가락을 잡을수 있으므로 젓가락을 잡을수 있는 권한이 있다는 뜻이다.

 

뮤텍스 변수는  철학자 state[5]이 배열이 공유변수 이기때문에 공유변수의 동시접근을 막기위한 lock 개념의 변수이다.

 

젓가락을 잡는 코드(pick up) 부터 살펴보자

우선 젓가락 잡는 코드를 호출하면 공유변수를 건들이기떄문에

뮤텍스 변수로 락을 걸어놓고 추후에 풀어주는 작업이있다.

일단 lock 를 걸고

본인의 상태를 hungry로 바꾸어 놓는다

다음 test 함수를 호출하는데  -> test 함수는 본인이 젓가락두개를 다 잡을 수있는 상태인지 확인하는 함수이다.

test함수에서는 조건을 검사하는데 왼쪽 오른쪽 모두 밥먹고있지않은 상태이며 본인이 hungry 상태인지 검사한다

-> 이세가지가 모두 만족해야지만 철학자의 상태를 eating 으로 바꿔주고 

그철학자의 젓가락 잡을수 잇는 권한을 준다.(V 연산을 통해서 1을 늘려준다.)

-> 여기서 특이한점이 대부분 세마포어 변수는 자원의 갯수를 세는데 여기선 권한을 나타내기 때문에 초기값이 0이고 권한을 줄때 1로 늘려준다.-> 세마포어의 철학에 잘 맞지 않는다.

그래서 이제 test 에서 빠져나오면 젓가락 권한이 1이므로 권한이있으니 그것을 P연산을 통해서 권한을 가져오면서 젓가락을 집는것을 표현한다.

이렇게 pick up 함수가 끝난다.

만약 다른 옆의 철학자가 밥먹고있었다면 권한을 얻지못하고 test함수가 끝난다.

-> 이경우에는 밑에서 파란색 밑에있는 P연산에서 권한이 없어서 대기하게 된다

-권한이 언제 생기는가? 다른 주변의 철학자가 밥을 다먹었을때 생기도록 젓가락을 내려놓을때 양옆의 철학자의 권한을 살펴보는 test함수를 실행시킨다.

 

젓가락을 내려놓는 코드(put down)를 보면
이제 state 를 thinking 으로 바꿔주면서 

test를 자신의 양옆에 걸어주면서 양쪽다 권한을 테스트해줘서 자신이 밥먹고있어서 pick up을 못했던것을 풀어주게 된다.

 

 

 

세마포어 코딩 쉽게 만들어 주긴하지만 그럼에도 불구하고 문제가 생겼을때 버그가 생겼을때 잡기가 쉽지않다.

 

->한번의 실수가 모든 시스템에 치명적인 영향을 미친다.

 

예를들어 크리티컬 섹션에 들어가기 전에 P연산을 하고 나올때 V연산을 해야하는데 실수로 반대로 했다고 보자

-> 그럼 일단 lock 을 거는것 기능 조차 깨지고 문제가 생긴다.

 

두번째 예로 나올때도 실수로 P연산을 해버린다면 영원히 그곳에서 빠져나올 수 없는 데드락에 걸린다.

lock이 반환이 안되고 계속 기다릴테니

 

세마포어 잘지킨다면 좋지만 실수하면 박살난다.

 

그래서 synchronization을 위해서 monitor라는것을 제공한다

 

다 추상적인 이야기인데 모니터는 프로그래밍 언어 차원에서 synchronization 문제를 해결하는 high-level-synchronization-construct이다.

객체지향 프로그래밍 언어를 보면 객체를 중심으로 연산자들이 정의되는것을 볼수 있다. 

그런것처럼 모니터는 공유데이터를 접근하기 위해서는 모니터라고 정의된 내부의 procedure(이름이 붙은 프로그램의 일부분)를 통해서만 공유데이터를 접근할수 있게 만들어놓은것이다.

 

 

애초에 자료처럼 

공유데이터랑 operations(뭔가 정의된 연산자들) 을 한 묶음으로 보는것이다.

그래서 공유데이터는 정의된 operations 을 통해서만 접근할수 있고 -> 이 각 연산들은 동시에 여러개가 실행되지 않도록 권한을 주는것이다

 

이렇게 되면 프로그래머 입장에서 lock을 걸 필요가 없어진다.

-> 세마포어의 경우 lock을 프로그래머가 걸어주는것인데 프로그래머가 안해도 되도록 해주는것이다.

모니터가 기본적으로 lock 가 걸고 푸는게 알아서 된다고 생각하면된다.

 

하나의 프로세스만 알아서 공유데이터에 접근하게 모니터가 관리해주고

나머지 프로세스들은 큐에 줄세우는 형태이다.

 

 

세마포어와의 차이점은 lock 을 걸 필요가 없는것이다.

보통 모니터를 보면 모니터 내부에 공유변수에대한 선언을 해놓고(위의 자료에서 shared variable declarations 라고 되어있는것처럼)

그리고 공유데이터에 접근하기위한 프로시저들은 모니터 내부함수로 구현해놓는다.

초기화함수도 구현되어있다 위의 자료를 보면 감이온다.

 

세마포어에서 자원의 갯수를 세는역할도 했는데 그것은 모니터에서는 condition 이라는 변수로 대체한다.

condition 의 x 라는 자원이 여분이있으면 바로 접근하게 해주고 여분이 없어서 기다리게 해야한다면

wait이라는 기다리는 함수가 정의되어있고

빠저나갈때는 signal 이라는 자원을 늘려주고나가는 함수가 정의되어있어 이것을 사용하면된다.

 

생산자 소비자 문제를 모니터로 사용한다면 이렇게 코드를 작성할수 있다.

여기서보면 lock 을 걸고 푸는것이 생략되어있다 lock 코드는 monitor 안에 정의되어있기 때문이다.

어짜피 모니터 내부코드를 호출하기 때문에 한가지로만 제한되어있다.

 

condition 으로 -> 자원의 갯수만 세어주는 역할함

 

 

이부분 다음강에서 더 설명한다함

 

 

 


 

https://steady-coding.tistory.com/557

 

[Java] 뮤텍스, 세마포어, 모니터

java-study에서 스터디를 진행하고 있습니다. 뮤텍스 (Mutual Exclusion) 멅리 프로그래밍 환경에서 자원에 대한 접근에 제한을 강제하기 위한 동기화 기법이다. 특징은 다음과 같다. Boolean 타입의 Lock

steady-coding.tistory.com

자바에서 모니터 쓰는것 설명