-
[운영체제] Inter Process Communication(프로세스 동기화) - 1운영체제 2021. 6. 9. 21:32
Inter Process Communication (IPC)
두 개 이상의 프로세스가 동시에 진행되는데 같은 메모리 주소를 참조한다면?
프로세스 A와 프로세스 B가 메모리주소 M의 변수 x를 참조하고 있다고 하자.
프로세스 A와 프로세스 B는 모두 Read & Write가 가능하다.
프로세스 A와 프로세스 B가 동시에 변수 x를 수정하는 경우 어떻게 될까?
1. 다행히 충돌이 없는 경우
- 프로세스 A가 M주소의 변수를 Read한다. (M주소에는 x)
- 프로세스 A가 변수(x)에 2를 더한다. (M주소에는 x)
- 프로세스 A가 (x + 2)를 M 주소에 저장한다 (M주소에는 x+2)
- 프로세스 B가 M주소의 변수를 Read한다. (M주소에는 x+2)
- 프로세스 B가 변수(x+2)에 3를 더한다. (M주소에는 x+2)
- 프로세스 B가 (x + 5)를 M 주소에 저장한다 (M주소에는 x+5)
- 결과 : x + 5
2. 충돌이 생기는 경우
- 프로세스 A가 M주소의 변수를 Read한다. (M주소에는 x)
- 프로세스 A가 변수(x)에 2를 더한다. (M주소에는 x)
- process scheduling에 의한, 또는 interrupt로 인해 context switch가 발생하여 CPU가 프로세스 B로 넘어감
- 프로세스 B가 M주소의 변수를 Read한다. (M주소에는 x)
- 프로세스 B가 변수(x)에 3를 더한다. (M주소에는 x)
- 프로세스 B가 (x + 3)를 M 주소에 저장한다 (M주소에는 x+3)
- 프로세스 B가 끝났으므로 context switch가 발생하여 CPU가 프로세스 A로 넘어감
- 프로세스 A가 (x + 2)를 M 주소에 저장한다 (M주소에는 x+2)
- 결과 : x + 2
2번의 경우 context switch가 일어나서, 같은 수정 전 자원을 읽어왔을 때 상대방이 수정을 하면 자원의 최종 수행 값이 달라져있음을 볼 수 있다. 이러한 것을 race condition 이라고 한다.
이는 Github에서 협업의 flow와 비슷하기도 하다. 두 사람이 동시에 pull을 받고 누구 하나가 먼저 push를 하면 다른 하나는 다시 pull을 받지 않으면 push를 할 수 없다.
따라서 프로세스는 메모리의 어떤 자원을 이용할 것이라면 그것을 '잠궈서' 자신이 사용하고 있음을 밝히고, 다른 프로세스가 접근할 수 없도록 해야 한다.
결론 : 동시에 한가지 자원에 접근하면 race condition이 발생할 수 있다. 방법을 찾자!
Lock 방식
접근 멈춰!
프로세스가 자원을 차지할 수 있는 상황일 때, Lock을 걸고 상대방이 접근할 수 없도록 하는 방식입니다.
Lock은 자원이 사용중인지를 나타내는 어떠한 변수를 True로 바꾸면서 걸 수 있습니다.
간단한 예시를 통해서 Lock의 방식을 보도록 하겠습니다.
- 당신은 지하철역에 들어와서 화장실에 왔습니다.
- 변기칸에서 사용가능 문구를 보고 이 변기칸이 사용 가능함을 인지합니다.
- 당신은 변기칸에 들어갑니다.
- 당신은 해당 칸의 잠금장치를 잠궈서 변기칸을 사용 중 문구로 바꿉니다.
2번처럼 프로세스는 해당 메모리 주소가 사용중인지를 확인하고 사용 가능함을 인지합니다. (11번째 줄)
3번처럼 프로세스는 해당 source에 접근하려고 합니다 (12번째 줄)
4번처럼 프로세스는 해당 메모리 주소를 사용중으로 바꿉니다. (2번째 줄)
이런 방식을 사용하면 당신의 화장실 칸이 잠겨 있으면 다른 사람은 밖에서 기다리게 됩니다. 즉 당신이 사용중일 때 interrupt가 발생하지 않고, 충돌이 일어나지 않는 것입니다.
이렇게 lock을 걸고 프로세스를 수행하는 것을 critical section이라고 합니다.
이 방식으로 해결이 되는 것처럼 보이지만, 사실 이 방식에는 헛점이 하나 있습니다. 상황을 하나 가정해보겠습니다.
- 당신이 칸에 들어갑니다.
- 당신이 문을 닫습니다.
- 당신이 해당 칸의 잠금장치를 잠굽........... 누가 문을 엽니다!
당신이 문을 닫았지만, 해당 칸은 잠금장치를 잠구기 전에는 아직 밖에서는 사용 가능으로 보이게 됩니다. 즉 밖에서 뒤늦게 도착한 사람은 잠궈지기 전까진 그 칸이 사용 가능하다고 생각하고 문을 열 여지가 있다는 것입니다.
프로세스의 경우에도 마찬가지입니다. 해당 메모리 주소의 값을 읽어온 후에 변수를 True로 변경하기 직전, 그 찰나의 순간에 interrupt가 들어온다면 context switch가 일어나고 이 전과 같은 충돌이 일어나게 되는 것입니다.
위의 코드에서는 11번째 줄에서 프로세스가 해당 자원이 사용 가능함을 알고, 12번으로 들어가서 lock함수의 2번째줄, held를 true로 만들기 직전에 interrupt가 들어온 것입니다.
충돌의 가능성은 적어졌지만 역시나 충돌의 가능성이 존재하기 때문에 시스템에 치명적일 수가 있습니다.
결론 : Lock 방식을 통해서 critical section을 생성해서 해결 해보려 했으나, 아직 race condition이 발생할 가능성이 존재한다.
TestandSet 방식
충돌 멈춰!
Lock 방식을 이용했을 때 충돌이 일어난 이유는 해당 자원이 사용 가능함을 인지 ~ 해당 자원을 내가 사용하고 있음을 변수로 표시 하는 사이에 interrupt가 일어났기 때문입니다.
그렇다면 다른 방식의 화장실을 통해서 해결해볼까요?
- 당신은 어떤 화장실에 도착했습니다.
- 화장실 문을 열고 들어가자마자 화장실 천장의 센서가 당신을 인식하고 해당 칸을 사용중으로 바꿉니다.
- 당신이 문을 닫고 문을 잠급니다.
화장실을 열고 들어감과 동시에 사용중으로 바뀝니다. 즉 당신이 잠그기 전에 찰나의 충돌의 순간이 있었던 것을 해결한 것입니다.
그렇다면 이 과정을 코드 상에서 한줄로 표현하면 어떻게 될까요?
Test and Set의 Test는 사용가능한지 확인 하는 것을 말하며, Set은 내가 사용하고 있음을 알리는 것을 말합니다.
16번째 줄의 while문의 TestandSet(Lock)은 자신이 해당 자원을 lock할때까지 반복 수행하며, 얻게되는 순간 lock을 걸면서 while문을 빠져나옵니다.
즉 TestandSet(lock) 함수를 통해서 확인과 함께 동시에 Lock을 건다는 것입니다.
Lock 방식에서는 두 줄로 이루어져서 사이에 틈이 있었지만, 이 경우 한줄로 모든 것을 해결합니다.
이러한 것을 Atomic Operation이라고 합니다.
- atom은 원자를 뜻하며, 화학에서는 성질을 유지하면서 더 이상 쪼갤 수 없는 물질 구조를 말합니다.
- 즉 해당 코드가 더 이상 쪼개질 수 없어서 틈이 생기지 않고, interrupt로 인해 충돌이 일어나지 않는다고 생각하시면 됩니다.
위의 코드를 보면 17번째 줄에 continue를 볼 수 있습니다.
즉 16~17번째 while문은 사실상 아무것도 하고 있지 않으면서, 자원을 얻기까지 계속해서 불러와지고 있으며, CPU를 낭비하고 있다는 뜻입니다.
이것을 Busy Waiting이라고 합니다.
이 Busy waiting은 CPU 낭비가 심하기 때문에 좋지 않은 쓰레드(프로세스) 동기화 방식입니다.
- 원래 프로세스 단위로 context switching이 일어났으나, 최근에는 쓰레드 단위로 context switching이 일어나므로 쓰레드 동기화라고 많이 부른다.
결론 : 드디어 race condition이 일어날 가능성을 배제했다! 근데 CPU 낭비가 심한데..?
이제 Busy Waiting으로 인한 CPU의 낭비를 없앨 방법인 Semaphore에 대해서는 다음 포스팅에서 설명하겠습니다.
'운영체제' 카테고리의 다른 글
[운영체제] Process 와 Thread (0) 2021.12.03 [운영체제] window 운영체제 (0) 2021.06.15 [운영체제] 프로세스 스케쥴링(Process Scheduling) (0) 2021.06.08 [운영체제] 컴퓨터의 부팅 (0) 2021.06.08 [운영체제] 운영체제란? (0) 2021.06.08