-
[운영체제 Step10] - 프로세스 동기화 & 상호배제 본문
프로세스 동기화라는 것이 있습니다.
우리가 사용하는 컴퓨터들, 운영체제를 보면 프로세스가 여러 개 존재합니다.
이런 시스템을 우리는 다중 프로그래밍 시스템이라고 부릅니다.
그리고 이런 시스템에서 프로세스들은 서로 독립적으로 동작합니다.
즉, 프로세스들이 동시에 동작할 수 있다고도 할 수 있겠습니다.
이런 환경에서 어떤 자원 하나를 두 프로세스에서 동시에 사용하려고 한다고 해봅시다. 그럼 어떻게 될까요?
상황에 따라서는 문제가 되지 않는 경우도 있지만 공유 자원을 동시에 사용하면 문제가 발생할 거라고 짐작할 수 있겠습니다.
예를들어 도화지가 하나 있고 두 사람이 동시에 그림을 그리려고 한다면, 어떻게 되죠??
우리가 원하지 않는 결과물이 나올 것입니다.
그래서 이런 일이 안생기려면 서로 협의를 통해 약속을 지켜야 할 것입니다.
프로세스에서도 프레세스끼리 대화를 통해 동작을 맞추는 것.
이것을 우리는 동기화(Synchronization) 라고 부릅니다.
이해하기 어려우시다면 프로세스끼리의 대화라고 이해하시면 되겠습니다.
그러나 프로세스끼리 처음 상황에 직면하기 전까지는 서로에 대해 잘 모를 수 있습니다.
이를 우리는 비동기적(Asynchronous)라고 부릅니다.
동기적의 반대의 의미를 가지고 있습니다.
그리고 병행적(Concurrent)라는 의미와 헷갈리실 수 있어서 설명드리겠습니다.
병행적이라는 의미는 여러 개의 프로세스들이 동시에 시스템에 존재할 때를 일컫습니다.
그러니까 하나의 시스템에 여러 프로세스들이 동시에 실행중일 때의 의미이지, 동기와 비동기의 여부가 아닙니다.
그래서 문제가 발생할 때는 다음과 같은 상황일 때 발생합니다.
병행 수행중인 비동기적인 프로세스들이 공유 자원에 동시에 접근할 때, 문제가 발생하게 됩니다.
이제 본격적으로 더 깊이 있게 들어가기 전에 몇 가지 용어만 짚고 넘어가겠습니다.
- Shared data (공유 데이터)
- Critical section (임계 영역)
- Mutual exclusion (상호 배제)
공유 데이터는 여러 프로세스들이 데이터를 공유하고 있을 때의 의미입니다. 만약 공유하고 있는 어느 곳에서 데이터를 손상시키면 데이터가 손상될 수 있습니다. 따라서 이를 Critical data라고도 부릅니다.
임계 영역은 공유 데이터를 접근하는 코드 영역(code segment)이라고 부릅니다.
그리고 상호 배제라는 것은 둘 이상의 프로세스가 동시에 임계 영역에 진입하는 것을 막는 것을 일컫습니다.
이 상호 배제라는 단어는 앞으로도 계속 나올 예정이니 기억해주시길 바랍니다.
자 이 그림을 한 번 보시겠습니다.
이 코드는 어떤 것을 의미하나요?
Pi에서는 sdata + 1을 해주고 있고 Pj에서도 sdata + 1을 해주고 있습니다.
그럼 메모리에는 뭐가 들어가게 될까요???
아마 기대하기로는 2가 되기를 기대할 것입니다.
하지만 이를 잘못 조정하면 기계어 명령의 특성을 크게 위배하는 행위가 될 수 있습니다.
원자성이란 더 이상 쪼갤 수 없다는 의미를 가지고 있습니다.
즉, 하나의 기계어 명령을 실행 도중에는 방해를 받지 않아야 합니다.
위의 코드를 보시면 Pi와 Pj의 코드에서 컴파일러가 이 코드 내용을 번역하게 될 것입니다.
그 결과
자, Pi의 1,2,3번과 Pj의 a,b,c는 각각 선점에 의해 순서가 그대로 실행되지 않을 수 있습니다.
따라서 우리가 기대하던 방향이 아닌, 1 -> 2 -> A -> B -> C -> 3 이런식으로 수행될 수 있다는거죠.
그럼 어떻게 될까요? 1 -> 2 까지는 데이터가 1이 추가되고 A -> B -> C 가 되면서 다른 1이 sdata에 저장되겠지요?
그러다가 3번이 실행되면 Pi에서도 1이, Pj에서도 1이 저장될 것입니다.
이렇게 되면 굳이 공유 데이터를 쓸 필요가 없어지게 됩니다.
우리가 바라고 있던 순서는 1 -> 2 -> 3 -> A -> B -> C 가 실행되어 sdata에는 2가 저장되길 바랬는데 말이죠.
즉 하나로 보장이 되어야 하는데 보장이 안될 수 있으니 실행 순서에 따라 결과가 달라지는 것을 우리는
Race Condition이라고 부릅니다. 즉, 달리는 상태에 따라 결과가 달라진다는 의미입니다.
그럼 우리가 원하는 결과를 도출하려면 어떻게 해야할까요??
여기서 바로 앞에서 배웠던 상호배제가 사용됩니다.
앞에서의 수행 과정의 선점이 안일어나게끔
"내가 이걸 하는 동안 아무도 못들어오게 막아줘 부탁할게" 라는 의미로서 사용됩니다.
그림에서도 P1이 사용하고 있으니 P2가 들어오는걸 막고 있네요.
이제부터 이것을 구현해보겠습니다.
(primitives는 "기본 연산"으로 이해하고 계시면 컴퓨터 공학 및 여러 전공 과목에서 큰 틀을 벗어나지 않습니다.)
즉, Mutual exclution primitives : 상호 배제 기본 연산
이것을 구현하기 위해 한 번 공부해보겠습니다.
- enterCS() Primitive
여러분이 화장실이 급할 때 공중화장실에 안에 사람이 있는지 모르지요?
그럴 땐 노크를 해서 사람이 있는지 여부를 확인하고 들어가야 합니다.
enterCS()역시 같은 의미로서 이해하시면 되겠습니다.
Critical section 진입하기 전에 미리 검사를 진행합니다.
- exitCS() Primitive
자 다시 화장실 예를 가져오겠습니다. 화장실에서 용무를 다하고 나올 때 줄 서있는 사람이 있다고 하겠습니다.
하지만 컴퓨터는 바보이기 때문에 저희가 들어오라고 하기 전까지 모릅니다. (참 소심하죠?)
그래서 exitCS()는 우리가 Critical section을 벗어날 때 시스템에 알려주어야 뒤의 enterCS()가 들어올 수 있습니다.
자 여러분에게 제가 enterCS()와 exitCS() 프로그래밍을 하라고 한다면 어떻게 하시겠어요?
네 일단 묻지도 따지지도 않고 만들어보라하면 무척 당황스러울 것입니다.
소프트웨어 개발에도 소프트웨어 명세, 기획, 구현, 설계 등 단계가 늘 존재하는 법이니까요.
그러므로 Mutual Exclusion 개발에도 역시 조건이 존재합니다.
첫 번째, CS에 프로세스가 있으면 다른 프로세스의 진입을 금지합니다.
두 번째, CS안에 있는 프로세스 외에는 다른 프로세스가 CS에 진입하는 것을 방해하면 안됩니다. (안에 비어있는데 다른 애가 프로세스 진입하는 걸 막으면 안되겠죠?
세 번째, 프로세스의 CS진입은 유한시간 내에 진입해야만 합니다. (계속 기다리고 있으면 안됩니다)
이 세가지 조건 잘 알아두시고, 이제 실제로 ME를 만들어 보겠습니다.
첫 번째 아이디어입니다.
중간의 turn은 음... 게임할 때 보시면 제 턴도 있고 상대방 턴도 있죠??
이걸 만든 아이디어는 turn마다 돌아가며 진행하겠다는 생각에서 따온 것입니다.
제일 먼저 repeat문을 쭉 실행합니다.
그리고 P0에서는 while(turn == 1) do endwhile; -> 이건 상대방 턴일 때 무엇을 하라는 의미이고
P1에서는 while(turn == 0) do endwhile; -> 이건 P0턴일 때 진행하라는 것이겠죠.
그리고 Critical Section에 들어간 이후 turn을 1을 주게 됩니다.
그런데 이것은 ME 조건에 만족할까요???
-> Progress 조건에 위배되므로 ME 조건을 만족하지 않습니다.
--> 왜냐하면 P0이 repeat문만 돌다가 Critical section에 진입하지 않을 수도 있고
--> P0이 turn을 1한테 주고 P1이 실행되기 전에 P0이 Critical Section에 진입하는 경우, 한 프로세스가 두 번 연속 CS에 진입하는 것은 불가능하기 때문에 이는 ME조건에 위배됩니다.
그러므로 이제 다른 버전으로 만들어보겠습니다.
이젠 턴 말고 플래그를 주자! 해서 flat를 2개의 인덱스로 나타내었습니다. (깃발과 똑같음)
P0입장에서 먼저, while(flag[1]) 이면 -> CS에 P1이 들어가 있다면
P0은 기다리게 될 것입니다.
그런데, P1이 없다면 (flat[1]이 false라면) CS안에 아무도 없으므로 flag[0] = true로 바꾸고 CS에 P0이 들어갑니다.
그리고 일이 끝나면 flag[0] = false로 바꾸고 P1으로 넘어가겠죠.
이 구문은 ME조건에 위배되지 않을까요?
이런 경우를 생각해보겠습니다.
P0이 먼저 들어와서 while문을 돌렸는데 flag[1]이 false여서 P0이 들어간다고 해봅시다.
그런데, Preemption (선점)을 당해서 잠깐 멈추었고 그와 동시에 P1이 도착을 해서 flag[0] == false이므로 P1이 CS에 들어간다고 해봅시다.
그럼 어떻게 되나요? 다시 원상태로 돌아온 P0은 그대로 실행될테고 flag[0] = true가 되어 CS에 P0도 들어갑니다.
이는 Mutual Exclusion 조건에 위배됩니다.
그럼 어떻게 해야 할까요?
세 번째 아이디어를 보겠습니다.
보아하니까 이전의 구문은 아무도 없는걸 확인하고 들어가려니 여러 상황에 오류가 날 수 있었으니 이번엔 본인이 들어가면 주변 모두에게 알리고 들어간다고 해봅시다.
먼저 flag[0] = true로 바꾸고 무작정 들어간다고 해봅시다.
그러나 그 사이 flag[1]도 동시에 들어왔고 하필이면 flag[0] = true라고 선언한 그 때 선점이 일어났다고 해봅시다.
그럼 flag[1]은 P0이 들어간 줄 알고 기다리고 있는데, P0입장에서는 선점에서 풀린뒤 가만 보니까 flag[1]이 true라서 계속while문을 실행할 것입니다.
결론적으로 CS에는 아무도 들어가있지 않은 상태가 되므로 이는 Progress, Bounded waiting 조건에 위배됩니다.
그러므로 제대로 해결하려면 좀 더 다른 조건을 생각해봐야 겠습니다.
너무 길어서 끊고 가겠습니다.
감사합니다.
'CS > 운영체제' 카테고리의 다른 글
[운영체제 Step12] - 상호배제 솔루션(2) (0) | 2021.12.14 |
---|---|
[운영체제 Step 11] - 상호배제 솔루션(1) (0) | 2021.12.13 |
[운영체제 Step 9] - 스케줄링 알고리즘 (SPN, SRTN, HRRN, MLQ, MFQ) (0) | 2021.12.09 |
[운영체제 Step8] - 프로세스 스케줄링 알고리즘 (0) | 2021.12.08 |
[운영체제 Step 7] - 프로세스 스케줄링 (0) | 2021.12.08 |